[cairo-commit] 6 commits - src/cairo-composite-rectangles.c src/cairo-image-source.c src/cairoint.h src/cairo-pattern.c src/cairo-pattern-private.h src/cairo-xcb-connection.c src/cairo-xcb-private.h src/cairo-xcb-surface-render.c src/cairo-xlib-core-compositor.c src/cairo-xlib-private.h src/cairo-xlib-source.c src/drm

Bryce Harrington bryce at kemper.freedesktop.org
Fri Oct 10 21:40:05 PDT 2014


 src/cairo-composite-rectangles.c |    2 
 src/cairo-image-source.c         |   22 +-
 src/cairo-pattern-private.h      |    5 
 src/cairo-pattern.c              |  303 +++++++++++++++++++++++++--------------
 src/cairo-xcb-connection.c       |   10 +
 src/cairo-xcb-private.h          |    6 
 src/cairo-xcb-surface-render.c   |   34 ++--
 src/cairo-xlib-core-compositor.c |    4 
 src/cairo-xlib-private.h         |    2 
 src/cairo-xlib-source.c          |   23 +-
 src/cairoint.h                   |    4 
 src/drm/cairo-drm-i915-shader.c  |   39 -----
 12 files changed, 263 insertions(+), 191 deletions(-)

New commits:
commit 45934f69cd158b7bb5632f5e4334a156795147f4
Author: Bill Spitzak <spitzak at gmail.com>
Date:   Thu Oct 9 19:46:16 2014 -0700

    image: Corrected extents calculations
    
    New implementations of _cairo_pattern_sampled_area and _cairo_pattern_get_extents
    which produce a more accurate bounding box. These do not depend on side-effects
    of analyze_filter, can handle different horizontal and vertical scales, filters
    wider than 1 for down-scaling, and compute a somewhat tighter bounding box
    in most cases.
    
    I removed the pad output of _cairo_pattern_analyze_filter as it is unused.
    
    Reviewed-by: Bryce Harrington <b.harrington at samsung.com>

diff --git a/src/cairo-composite-rectangles.c b/src/cairo-composite-rectangles.c
index e6639d0..6c3e97d 100644
--- a/src/cairo-composite-rectangles.c
+++ b/src/cairo-composite-rectangles.c
@@ -57,7 +57,7 @@ _cairo_composite_reduce_pattern (const cairo_pattern_t *src,
     if (dst->base.type == CAIRO_PATTERN_TYPE_SOLID)
 	return;
 
-    dst->base.filter = _cairo_pattern_analyze_filter (&dst->base, NULL),
+    dst->base.filter = _cairo_pattern_analyze_filter (&dst->base);
 
     tx = ty = 0;
     if (_cairo_matrix_is_pixman_translation (&dst->base.matrix,
diff --git a/src/cairo-pattern-private.h b/src/cairo-pattern-private.h
index bbcfadd..be8ab9f 100644
--- a/src/cairo-pattern-private.h
+++ b/src/cairo-pattern-private.h
@@ -289,7 +289,7 @@ _cairo_mesh_pattern_coord_box (const cairo_mesh_pattern_t *mesh,
 			       double                     *out_xmax,
 			       double                     *out_ymax);
 
-cairo_private_no_warn cairo_filter_t
+cairo_private void
 _cairo_pattern_sampled_area (const cairo_pattern_t *pattern,
 			     const cairo_rectangle_int_t *extents,
 			     cairo_rectangle_int_t *sample);
@@ -328,6 +328,9 @@ cairo_private cairo_bool_t
 _cairo_pattern_equal (const cairo_pattern_t *a,
 		      const cairo_pattern_t *b);
 
+cairo_private cairo_filter_t
+_cairo_pattern_analyze_filter (const cairo_pattern_t *pattern);
+
 /* cairo-mesh-pattern-rasterizer.c */
 
 cairo_private void
diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c
index 1a93d2b..e16823d 100644
--- a/src/cairo-pattern.c
+++ b/src/cairo-pattern.c
@@ -28,8 +28,6 @@
  *	    Carl Worth <cworth at cworth.org>
  */
 
-#include "cairoint.h"
-
 #include "cairo-array-private.h"
 #include "cairo-error-private.h"
 #include "cairo-freed-pool-private.h"
@@ -3361,24 +3359,13 @@ use_bilinear(double x, double y, double t)
 /**
  * _cairo_pattern_analyze_filter:
  * @pattern: surface pattern
- * @pad_out: location to store necessary padding in the source image, or %NULL
  * Returns: the optimized #cairo_filter_t to use with @pattern.
  *
- * Analyze the filter to determine how much extra needs to be sampled
- * from the source image to account for the filter radius and whether
- * we can optimize the filter to a simpler value.
- *
- * XXX: We don't actually have any way of querying the backend for
- *      the filter radius, so we just guess base on what we know that
- *      backends do currently (see bug #10508)
+ * Possibly optimize the filter to a simpler value depending on transformation
  **/
 cairo_filter_t
-_cairo_pattern_analyze_filter (const cairo_pattern_t	*pattern,
-			       double			*pad_out)
+_cairo_pattern_analyze_filter (const cairo_pattern_t *pattern)
 {
-    double pad;
-    cairo_filter_t optimized_filter;
-
     switch (pattern->filter) {
     case CAIRO_FILTER_GOOD:
     case CAIRO_FILTER_BEST:
@@ -3389,15 +3376,8 @@ _cairo_pattern_analyze_filter (const cairo_pattern_t	*pattern,
 	 * will cause blurriness)
 	 */
 	if (_cairo_matrix_is_pixel_exact (&pattern->matrix)) {
-	    pad = 0.;
-	    optimized_filter = CAIRO_FILTER_NEAREST;
+	    return CAIRO_FILTER_NEAREST;
 	} else {
-	    /* 0.5 is enough for a bilinear filter. It's possible we
-	     * should defensively use more for CAIRO_FILTER_BEST, but
-	     * without a single example, it's hard to know how much
-	     * more would be defensive...
-	     */
-	    pad = 0.5;
 	    /* Use BILINEAR for any scale greater than .75 instead
 	     * of GOOD. For scales of 1 and larger this is identical,
 	     * for the smaller sizes it was judged that the artifacts
@@ -3410,73 +3390,126 @@ _cairo_pattern_analyze_filter (const cairo_pattern_t	*pattern,
 			      pattern->matrix.x0) &&
 		use_bilinear (pattern->matrix.yx, pattern->matrix.yy,
 			      pattern->matrix.y0))
-		optimized_filter = CAIRO_FILTER_BILINEAR;
-	    else
-		optimized_filter = pattern->filter;
+		return CAIRO_FILTER_BILINEAR;
 	}
 	break;
 
     case CAIRO_FILTER_NEAREST:
     case CAIRO_FILTER_GAUSSIAN:
     default:
-	pad = 0.;
-	optimized_filter = pattern->filter;
 	break;
     }
 
-    if (pad_out)
-	*pad_out = pad;
+    return pattern->filter;
+}
 
-    return optimized_filter;
+/**
+ * _cairo_hypot:
+ * Returns: value similar to hypot(@x, at y)
+ *
+ * May want to replace this with Manhattan distance (abs(x)+abs(y)) if
+ * hypot is too slow, as there is no need for accuracy here.
+ **/
+static inline double
+_cairo_hypot(double x, double y)
+{
+    return hypot(x, y);
 }
 
-cairo_filter_t
+/**
+ * _cairo_pattern_sampled_area
+ *
+ * Return region of @pattern that will be sampled to fill @extents,
+ * based on the transformation and filter.
+ *
+ * This does not include pixels that are mulitiplied by values very
+ * close to zero by the ends of filters. This is so that transforms
+ * that should be the identity or 90 degree rotations do not expand
+ * the source unexpectedly.
+ *
+ * XXX: We don't actually have any way of querying the backend for
+ *      the filter radius, so we just guess base on what we know that
+ *      backends do currently (see bug #10508)
+ **/
+void
 _cairo_pattern_sampled_area (const cairo_pattern_t *pattern,
 			     const cairo_rectangle_int_t *extents,
 			     cairo_rectangle_int_t *sample)
 {
-    cairo_filter_t filter;
     double x1, x2, y1, y2;
-    double pad;
+    double padx, pady;
 
-    filter = _cairo_pattern_analyze_filter (pattern, &pad);
-    if (pad == 0.0 && _cairo_matrix_is_identity (&pattern->matrix)) {
+    /* Assume filters are interpolating, which means identity
+       cannot change the image */
+    if (_cairo_matrix_is_identity (&pattern->matrix)) {
 	*sample = *extents;
-	return filter;
+	return;
     }
 
-    x1 = extents->x;
-    y1 = extents->y;
-    x2 = extents->x + (int) extents->width;
-    y2 = extents->y + (int) extents->height;
-
+    /* Transform the centers of the corner pixels */
+    x1 = extents->x + 0.5;
+    y1 = extents->y + 0.5;
+    x2 = x1 + (extents->width - 1);
+    y2 = y1 + (extents->height - 1);
     _cairo_matrix_transform_bounding_box (&pattern->matrix,
 					  &x1, &y1, &x2, &y2,
 					  NULL);
-    if (x1 > CAIRO_RECT_INT_MIN)
-	sample->x = floor (x1 - pad);
-    else
-	sample->x = CAIRO_RECT_INT_MIN;
 
-    if (y1 > CAIRO_RECT_INT_MIN)
-	sample->y = floor (y1 - pad);
-    else
-	sample->y = CAIRO_RECT_INT_MIN;
+    /* How far away from center will it actually sample?
+     * This is the distance from a transformed pixel center to the
+     * furthest sample of reasonable size.
+     */
+    switch (pattern->filter) {
+    case CAIRO_FILTER_NEAREST:
+    case CAIRO_FILTER_FAST:
+	/* Correct value is zero, but when the sample is on an integer
+	 * it is unknown if the backend will sample the pixel to the
+	 * left or right. This value makes it include both possible pixels.
+	 */
+	padx = pady = 0.004;
+	break;
+    case CAIRO_FILTER_BILINEAR:
+    case CAIRO_FILTER_GAUSSIAN:
+    default:
+	/* Correct value is .5 */
+	padx = pady = 0.495;
+	break;
+    case CAIRO_FILTER_GOOD:
+	/* Correct value is max(width,1)*.5 */
+	padx = _cairo_hypot (pattern->matrix.xx, pattern->matrix.xy);
+	if (padx <= 1.0) padx = 0.495;
+	else if (padx >= 16.0) padx = 7.92;
+	else padx *= 0.495;
+	pady = _cairo_hypot (pattern->matrix.yx, pattern->matrix.yy);
+	if (pady <= 1.0) pady = 0.495;
+	else if (pady >= 16.0) pady = 7.92;
+	else pady *= 0.495;
+	break;
+    case CAIRO_FILTER_BEST:
+	/* Correct value is width*2 */
+	padx = _cairo_hypot (pattern->matrix.xx, pattern->matrix.xy) * 1.98;
+	if (padx > 7.92) padx = 7.92;
+	pady = _cairo_hypot (pattern->matrix.yx, pattern->matrix.yy) * 1.98;
+	if (pady > 7.92) pady = 7.92;
+	break;
+    }
 
-    if (x2 < CAIRO_RECT_INT_MAX)
-	sample->width = ceil (x2 + pad);
-    else
-	sample->width = CAIRO_RECT_INT_MAX;
+    /* round furthest samples to edge of pixels */
+    x1 = floor (x1 - padx);
+    if (x1 < CAIRO_RECT_INT_MIN) x1 = CAIRO_RECT_INT_MIN;
+    sample->x = x1;
 
-    if (y2 < CAIRO_RECT_INT_MAX)
-	sample->height = ceil (y2 + pad);
-    else
-	sample->height = CAIRO_RECT_INT_MAX;
+    y1 = floor (y1 - pady);
+    if (y1 < CAIRO_RECT_INT_MIN) y1 = CAIRO_RECT_INT_MIN;
+    sample->y = y1;
 
-    sample->width  -= sample->x;
-    sample->height -= sample->y;
+    x2 = floor (x2 + padx) + 1.0;
+    if (x2 > CAIRO_RECT_INT_MAX) x2 = CAIRO_RECT_INT_MAX;
+    sample->width = x2 - x1;
 
-    return filter;
+    y2 = floor (y2 + pady) + 1.0;
+    if (y2 > CAIRO_RECT_INT_MAX) y2 = CAIRO_RECT_INT_MAX;
+    sample->height = y2 - y1;
 }
 
 /**
@@ -3496,7 +3529,9 @@ _cairo_pattern_get_extents (const cairo_pattern_t         *pattern,
 			    cairo_rectangle_int_t         *extents)
 {
     double x1, y1, x2, y2;
-    cairo_status_t status;
+    int ix1, ix2, iy1, iy2;
+    cairo_bool_t round_x = FALSE;
+    cairo_bool_t round_y = FALSE;
 
     switch (pattern->type) {
     case CAIRO_PATTERN_TYPE_SOLID:
@@ -3508,7 +3543,6 @@ _cairo_pattern_get_extents (const cairo_pattern_t         *pattern,
 	    const cairo_surface_pattern_t *surface_pattern =
 		(const cairo_surface_pattern_t *) pattern;
 	    cairo_surface_t *surface = surface_pattern->surface;
-	    double pad;
 
 	    if (! _cairo_surface_get_extents (surface, &surface_extents))
 		goto UNBOUNDED;
@@ -3519,14 +3553,12 @@ _cairo_pattern_get_extents (const cairo_pattern_t         *pattern,
 	    if (pattern->extend != CAIRO_EXTEND_NONE)
 		goto UNBOUNDED;
 
-	    /* The filter can effectively enlarge the extents of the
-	     * pattern, so extend as necessary.
-	     */
-	    _cairo_pattern_analyze_filter (&surface_pattern->base, &pad);
-	    x1 = surface_extents.x - pad;
-	    y1 = surface_extents.y - pad;
-	    x2 = surface_extents.x + (int) surface_extents.width  + pad;
-	    y2 = surface_extents.y + (int) surface_extents.height + pad;
+	    x1 = surface_extents.x;
+	    y1 = surface_extents.y;
+	    x2 = surface_extents.x + (int) surface_extents.width;
+	    y2 = surface_extents.y + (int) surface_extents.height;
+
+	    goto HANDLE_FILTER;
 	}
 	break;
 
@@ -3534,7 +3566,6 @@ _cairo_pattern_get_extents (const cairo_pattern_t         *pattern,
 	{
 	    const cairo_raster_source_pattern_t *raster =
 		(const cairo_raster_source_pattern_t *) pattern;
-	    double pad;
 
 	    if (raster->extents.width == 0 || raster->extents.height == 0)
 		goto EMPTY;
@@ -3542,14 +3573,41 @@ _cairo_pattern_get_extents (const cairo_pattern_t         *pattern,
 	    if (pattern->extend != CAIRO_EXTEND_NONE)
 		goto UNBOUNDED;
 
-	    /* The filter can effectively enlarge the extents of the
-	     * pattern, so extend as necessary.
-	     */
-	    _cairo_pattern_analyze_filter (pattern, &pad);
-	    x1 = raster->extents.x - pad;
-	    y1 = raster->extents.y - pad;
-	    x2 = raster->extents.x + (int) raster->extents.width  + pad;
-	    y2 = raster->extents.y + (int) raster->extents.height + pad;
+	    x1 = raster->extents.x;
+	    y1 = raster->extents.y;
+	    x2 = raster->extents.x + (int) raster->extents.width;
+	    y2 = raster->extents.y + (int) raster->extents.height;
+	}
+    HANDLE_FILTER:
+	switch (pattern->filter) {
+	case CAIRO_FILTER_NEAREST:
+	case CAIRO_FILTER_FAST:
+	    round_x = round_y = TRUE;
+	    /* We don't know which way .5 will go, so fudge it slightly. */
+	    x1 -= 0.004;
+	    y1 -= 0.004;
+	    x2 += 0.004;
+	    y2 += 0.004;
+	    break;
+	case CAIRO_FILTER_BEST:
+	    /* Assume best filter will produce nice antialiased edges */
+	    break;
+	case CAIRO_FILTER_BILINEAR:
+	case CAIRO_FILTER_GAUSSIAN:
+	case CAIRO_FILTER_GOOD:
+	default:
+	    /* These filters can blur the edge out 1/2 pixel when scaling up */
+	    if (_cairo_hypot (pattern->matrix.xx, pattern->matrix.yx) < 1.0) {
+		x1 -= 0.5;
+		x2 += 0.5;
+		round_x = TRUE;
+	    }
+	    if (_cairo_hypot (pattern->matrix.xy, pattern->matrix.yy) < 1.0) {
+		y1 -= 0.5;
+		y2 += 0.5;
+		round_y = TRUE;
+	    }
+	    break;
 	}
 	break;
 
@@ -3621,6 +3679,10 @@ _cairo_pattern_get_extents (const cairo_pattern_t         *pattern,
 	    } else {
 		goto  UNBOUNDED;
 	    }
+
+	    /* The current linear renderer just point-samples in the middle
+	       of the pixels, similar to the NEAREST filter: */
+	    round_x = round_y = TRUE;
 	}
 	break;
 
@@ -3628,22 +3690,8 @@ _cairo_pattern_get_extents (const cairo_pattern_t         *pattern,
 	{
 	    const cairo_mesh_pattern_t *mesh =
 		(const cairo_mesh_pattern_t *) pattern;
-	    double padx, pady;
-	    cairo_bool_t is_valid;
-
-	    is_valid = _cairo_mesh_pattern_coord_box (mesh, &x1, &y1, &x2, &y2);
-	    if (!is_valid)
+	    if (! _cairo_mesh_pattern_coord_box (mesh, &x1, &y1, &x2, &y2))
 		goto EMPTY;
-
-	    padx = pady = 1.;
-	    cairo_matrix_transform_distance (&pattern->matrix, &padx, &pady);
-	    padx = fabs (padx);
-	    pady = fabs (pady);
-
-	    x1 -= padx;
-	    y1 -= pady;
-	    x2 += padx;
-	    y2 += pady;
 	}
 	break;
 
@@ -3656,6 +3704,7 @@ _cairo_pattern_get_extents (const cairo_pattern_t         *pattern,
 	y1 -= pattern->matrix.y0; y2 -= pattern->matrix.y0;
     } else {
 	cairo_matrix_t imatrix;
+	cairo_status_t status;
 
 	imatrix = pattern->matrix;
 	status = cairo_matrix_invert (&imatrix);
@@ -3667,22 +3716,34 @@ _cairo_pattern_get_extents (const cairo_pattern_t         *pattern,
 					      NULL);
     }
 
-    x1 = floor (x1);
+    if (!round_x) {
+	x1 -= 0.5;
+	x2 += 0.5;
+    }
     if (x1 < CAIRO_RECT_INT_MIN)
-	x1 = CAIRO_RECT_INT_MIN;
-    y1 = floor (y1);
-    if (y1 < CAIRO_RECT_INT_MIN)
-	y1 = CAIRO_RECT_INT_MIN;
-
-    x2 = ceil (x2);
+	ix1 = CAIRO_RECT_INT_MIN;
+    else 
+	ix1 = _cairo_lround (x1);
     if (x2 > CAIRO_RECT_INT_MAX)
-	x2 = CAIRO_RECT_INT_MAX;
-    y2 = ceil (y2);
+	ix2 = CAIRO_RECT_INT_MAX;
+    else
+	ix2 = _cairo_lround (x2);
+    extents->x = ix1; extents->width  = ix2 - ix1;
+
+    if (!round_y) {
+	y1 -= 0.5;
+	y2 += 0.5;
+    }
+    if (y1 < CAIRO_RECT_INT_MIN)
+	iy1 = CAIRO_RECT_INT_MIN;
+    else
+	iy1 = _cairo_lround (y1);
     if (y2 > CAIRO_RECT_INT_MAX)
-	y2 = CAIRO_RECT_INT_MAX;
+	iy2 = CAIRO_RECT_INT_MAX;
+    else
+	iy2 = _cairo_lround (y2);
+    extents->y = iy1; extents->height = iy2 - iy1;
 
-    extents->x = x1; extents->width  = x2 - x1;
-    extents->y = y1; extents->height = y2 - y1;
     return;
 
   UNBOUNDED:
diff --git a/src/cairo-xlib-core-compositor.c b/src/cairo-xlib-core-compositor.c
index 9398079..5babcc8 100644
--- a/src/cairo-xlib-core-compositor.c
+++ b/src/cairo-xlib-core-compositor.c
@@ -292,9 +292,7 @@ render_boxes (cairo_xlib_surface_t	*dst,
 	      const cairo_pattern_t	*pattern,
 	      cairo_boxes_t		*boxes)
 {
-    double pad;
-
-    if (_cairo_pattern_analyze_filter (pattern, &pad) != CAIRO_FILTER_NEAREST)
+    if (pattern->filter != CAIRO_FILTER_NEAREST)
 	return fallback_boxes (dst, pattern, boxes);
 
     switch (pattern->extend) {
diff --git a/src/cairoint.h b/src/cairoint.h
index 75b34d0..b4e8ac8 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -2009,10 +2009,6 @@ slim_hidden_proto (cairo_surface_write_to_png_stream);
 
 #endif
 
-cairo_private_no_warn cairo_filter_t
-_cairo_pattern_analyze_filter (const cairo_pattern_t	*pattern,
-			       double			*pad_out);
-
 CAIRO_END_DECLS
 
 #include "cairo-mutex-private.h"
diff --git a/src/drm/cairo-drm-i915-shader.c b/src/drm/cairo-drm-i915-shader.c
index a1911d0..85aa984 100644
--- a/src/drm/cairo-drm-i915-shader.c
+++ b/src/drm/cairo-drm-i915-shader.c
@@ -1467,42 +1467,6 @@ i915_shader_acquire_solid_surface (i915_shader_t *shader,
     return CAIRO_STATUS_SUCCESS;
 }
 
-static cairo_filter_t
-sampled_area (const cairo_surface_pattern_t *pattern,
-	      const cairo_rectangle_int_t *extents,
-	      cairo_rectangle_int_t *sample)
-{
-    cairo_rectangle_int_t surface_extents;
-    cairo_filter_t filter;
-    double x1, x2, y1, y2;
-    double pad;
-
-    x1 = extents->x;
-    y1 = extents->y;
-    x2 = extents->x + (int) extents->width;
-    y2 = extents->y + (int) extents->height;
-
-    if (_cairo_matrix_is_translation (&pattern->base.matrix)) {
-	x1 += pattern->base.matrix.x0; x2 += pattern->base.matrix.x0;
-	y1 += pattern->base.matrix.y0; y2 += pattern->base.matrix.y0;
-    } else {
-	_cairo_matrix_transform_bounding_box (&pattern->base.matrix,
-					      &x1, &y1, &x2, &y2,
-					      NULL);
-    }
-
-    filter = _cairo_pattern_analyze_filter (&pattern->base, &pad);
-    sample->x = floor (x1 - pad);
-    sample->y = floor (y1 - pad);
-    sample->width  = ceil (x2 + pad) - sample->x;
-    sample->height = ceil (y2 + pad) - sample->y;
-
-    if (_cairo_surface_get_extents (pattern->surface, &surface_extents))
-	_cairo_rectangle_intersect (sample, &surface_extents);
-
-    return filter;
-}
-
 static cairo_status_t
 i915_shader_acquire_surface (i915_shader_t *shader,
 			     union i915_shader_channel *src,
@@ -1524,7 +1488,8 @@ i915_shader_acquire_surface (i915_shader_t *shader,
 
     extend = pattern->base.extend;
     src->base.matrix = pattern->base.matrix;
-    filter = sampled_area (pattern, extents, &sample);
+    filter = pattern->base.filter;
+    _cairo_pattern_sampled_area(&pattern->base, extents, sample);
 
     if (surface->type == CAIRO_SURFACE_TYPE_DRM) {
 	if (surface->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
commit 3cd7ed1f320c3ce330da3d3153c39b613e4a5a15
Author: Bill Spitzak <spitzak at gmail.com>
Date:   Thu Oct 9 19:46:15 2014 -0700

    xcb: Use image fallback for GOOD/BEST filters
    
    It will not use the fallback if the symbols in the previous patch
    are turned on.
    
    Also some code rearrangement to make this resemble the xlib version
    more and to remove some suspect bugs. In particular meshes should not
    work just because the translation is an integer.
    
    Reviewed-by: Bryce Harrington <b.harrington at samsung.com>

diff --git a/src/cairo-xcb-surface-render.c b/src/cairo-xcb-surface-render.c
index edfa34c..21a89cc 100644
--- a/src/cairo-xcb-surface-render.c
+++ b/src/cairo-xcb-surface-render.c
@@ -394,11 +394,6 @@ _pattern_is_supported (uint32_t flags,
     if (pattern->type == CAIRO_PATTERN_TYPE_SOLID)
 	return TRUE;
 
-    if (! _cairo_matrix_is_integer_translation (&pattern->matrix, NULL, NULL)) {
-	if ((flags & CAIRO_XCB_RENDER_HAS_PICTURE_TRANSFORM) == 0)
-	    return FALSE;
-    }
-
     switch (pattern->extend) {
     default:
 	ASSERT_NOT_REACHED;
@@ -412,18 +407,22 @@ _pattern_is_supported (uint32_t flags,
     }
 
     if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
-	cairo_filter_t filter;
-
-	filter = pattern->filter;
-	if (_cairo_matrix_is_pixel_exact (&pattern->matrix))
-	{
-	    filter = CAIRO_FILTER_NEAREST;
-	}
-
-	if (! (filter == CAIRO_FILTER_NEAREST || filter == CAIRO_FILTER_FAST)) {
-	    if ((flags & CAIRO_XCB_RENDER_HAS_FILTERS) == 0)
-		return FALSE;
+	switch (pattern->filter) {
+	case CAIRO_FILTER_FAST:
+	case CAIRO_FILTER_NEAREST:
+	    return (flags & CAIRO_XCB_RENDER_HAS_PICTURE_TRANSFORM) ||
+		_cairo_matrix_is_integer_translation (&pattern->matrix, NULL, NULL);
+	case CAIRO_FILTER_GOOD:
+	    return flags & CAIRO_XCB_RENDER_HAS_FILTER_GOOD;
+	case CAIRO_FILTER_BEST:
+	    return flags & CAIRO_XCB_RENDER_HAS_FILTER_BEST;
+	case CAIRO_FILTER_BILINEAR:
+	case CAIRO_FILTER_GAUSSIAN:
+	default:
+	    return flags & CAIRO_XCB_RENDER_HAS_FILTERS;
 	}
+    } else if (pattern->type == CAIRO_PATTERN_TYPE_MESH) {
+	return FALSE;
     } else { /* gradient */
 	if ((flags & CAIRO_XCB_RENDER_HAS_GRADIENTS) == 0)
 	    return FALSE;
@@ -435,9 +434,8 @@ _pattern_is_supported (uint32_t flags,
 	{
 	    return FALSE;
 	}
+	return TRUE;
     }
-
-    return pattern->type != CAIRO_PATTERN_TYPE_MESH;
 }
 
 static void
commit ca9aee4e62d914b3cd3d6747ff1a4961d9aff8ed
Author: Bill Spitzak <spitzak at gmail.com>
Date:   Thu Oct 9 19:46:14 2014 -0700

    xcb: Add switches for whether XRender supports GOOD/BEST filtering
    
    Currently these are always false, but if XRender use a pixman that
    supports filtering these could be turned on for that version.
    
    Reviewed-by: Bryce Harrington <b.harrington at samsung.com>

diff --git a/src/cairo-xcb-connection.c b/src/cairo-xcb-connection.c
index b48add1..2d51e14 100644
--- a/src/cairo-xcb-connection.c
+++ b/src/cairo-xcb-connection.c
@@ -77,6 +77,8 @@ typedef struct _cairo_xcb_xid {
 
 #define XCB_RENDER_HAS_PICTURE_TRANSFORM(surface)	XCB_RENDER_AT_LEAST((surface), 0, 6)
 #define XCB_RENDER_HAS_FILTERS(surface)			XCB_RENDER_AT_LEAST((surface), 0, 6)
+#define XCB_RENDER_HAS_FILTER_GOOD(surface) FALSE
+#define XCB_RENDER_HAS_FILTER_BEST(surface) FALSE
 
 #define XCB_RENDER_HAS_EXTENDED_REPEAT(surface)	XCB_RENDER_AT_LEAST((surface), 0, 10)
 #define XCB_RENDER_HAS_GRADIENTS(surface)	XCB_RENDER_AT_LEAST((surface), 0, 10)
@@ -390,6 +392,12 @@ _cairo_xcb_connection_query_render (cairo_xcb_connection_t *connection)
     if (XCB_RENDER_HAS_FILTERS (version))
 	connection->flags |= CAIRO_XCB_RENDER_HAS_FILTERS;
 
+    if (XCB_RENDER_HAS_FILTER_GOOD (version))
+	connection->flags |= CAIRO_XCB_RENDER_HAS_FILTER_GOOD;
+
+    if (XCB_RENDER_HAS_FILTER_BEST (version))
+	connection->flags |= CAIRO_XCB_RENDER_HAS_FILTER_BEST;
+
     if (XCB_RENDER_HAS_PDF_OPERATORS (version))
 	connection->flags |= CAIRO_XCB_RENDER_HAS_PDF_OPERATORS;
 
@@ -882,6 +890,8 @@ cairo_xcb_device_debug_cap_xrender_version (cairo_device_t *device,
 			       CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS |
 			       CAIRO_XCB_RENDER_HAS_PICTURE_TRANSFORM |
 			       CAIRO_XCB_RENDER_HAS_FILTERS |
+			       CAIRO_XCB_RENDER_HAS_FILTER_GOOD |
+			       CAIRO_XCB_RENDER_HAS_FILTER_BEST |
 			       CAIRO_XCB_RENDER_HAS_PDF_OPERATORS |
 			       CAIRO_XCB_RENDER_HAS_EXTENDED_REPEAT |
 			       CAIRO_XCB_RENDER_HAS_GRADIENTS);
diff --git a/src/cairo-xcb-private.h b/src/cairo-xcb-private.h
index 2610458..1e1d1ee 100644
--- a/src/cairo-xcb-private.h
+++ b/src/cairo-xcb-private.h
@@ -259,6 +259,8 @@ enum {
     CAIRO_XCB_RENDER_HAS_PDF_OPERATORS		= 0x0080,
     CAIRO_XCB_RENDER_HAS_EXTENDED_REPEAT	= 0x0100,
     CAIRO_XCB_RENDER_HAS_GRADIENTS		= 0x0200,
+    CAIRO_XCB_RENDER_HAS_FILTER_GOOD		= 0x0400,
+    CAIRO_XCB_RENDER_HAS_FILTER_BEST		= 0x0800,
 
     CAIRO_XCB_HAS_SHM				= 0x80000000,
 
@@ -271,7 +273,9 @@ enum {
 			    CAIRO_XCB_RENDER_HAS_FILTERS |
 			    CAIRO_XCB_RENDER_HAS_PDF_OPERATORS |
 			    CAIRO_XCB_RENDER_HAS_EXTENDED_REPEAT |
-			    CAIRO_XCB_RENDER_HAS_GRADIENTS,
+			    CAIRO_XCB_RENDER_HAS_GRADIENTS |
+			    CAIRO_XCB_RENDER_HAS_FILTER_GOOD |
+			    CAIRO_XCB_RENDER_HAS_FILTER_BEST,
     CAIRO_XCB_SHM_MASK    = CAIRO_XCB_HAS_SHM
 };
 
commit 58728da6eba4d89ba7c4328b22a552af633abb05
Author: Bill Spitzak <spitzak at gmail.com>
Date:   Thu Oct 9 19:46:13 2014 -0700

    xlib: Use image fallback for GOOD/BEST filters
    
    Fallback is not used if the symbols defined in the previous patch to
    indicate if XRender does GOOD/BEST are true.
    
    This patch also includes some changes to take advantage of the fact that
    if there is an integer translation analyze_filter will already have set
    the filter to NEAREST.
    
    Reviewed-by: Bryce Harrington <b.harrington at samsung.com>

diff --git a/src/cairo-xlib-source.c b/src/cairo-xlib-source.c
index 8275da3..81cc028 100644
--- a/src/cairo-xlib-source.c
+++ b/src/cairo-xlib-source.c
@@ -1093,17 +1093,22 @@ pattern_is_supported (cairo_xlib_display_t *display,
 	    return FALSE;
     }
 
-    if (! CAIRO_RENDER_HAS_PICTURE_TRANSFORM (display)) {
-	if (!_cairo_matrix_is_integer_translation (&pattern->matrix, NULL, NULL))
-	    return FALSE;
-    }
-
-    if (! CAIRO_RENDER_HAS_FILTERS (display)) {
-	    /* No filters implies no transforms, so we optimise away BILINEAR */
+    switch (pattern->filter) {
+    case CAIRO_FILTER_FAST:
+    case CAIRO_FILTER_NEAREST:
+	return CAIRO_RENDER_HAS_PICTURE_TRANSFORM (display) ||
+	    _cairo_matrix_is_integer_translation (&pattern->matrix, NULL, NULL);
+    case CAIRO_FILTER_GOOD:
+	return CAIRO_RENDER_HAS_FILTER_GOOD (display);
+    case CAIRO_FILTER_BEST:
+	return CAIRO_RENDER_HAS_FILTER_BEST (display);
+    case CAIRO_FILTER_BILINEAR:
+    case CAIRO_FILTER_GAUSSIAN:
+    default:
+	return CAIRO_RENDER_HAS_FILTERS (display);
     }
-
-    return TRUE;
 }
+
 cairo_surface_t *
 _cairo_xlib_source_create_for_pattern (cairo_surface_t *_dst,
 				       const cairo_pattern_t *pattern,
commit c653dcd3e6ac1cef2440fa0db97a8e5ce30b2a47
Author: Bill Spitzak <spitzak at gmail.com>
Date:   Thu Oct 9 19:46:12 2014 -0700

    xlib: Add symbols to indicate if XRender supports GOOD/BEST filtering
    
    Currently these are always false, but if some version of xlib uses
    a pixman supporting filtering they could be changed to return true
    for that version.
    
    Reviewed-by: Bryce Harrington <b.harrington at samsung.com>

diff --git a/src/cairo-xlib-private.h b/src/cairo-xlib-private.h
index 822c85b..6d37896 100644
--- a/src/cairo-xlib-private.h
+++ b/src/cairo-xlib-private.h
@@ -373,6 +373,8 @@ _cairo_xlib_font_close (cairo_xlib_font_t *font);
 
 #define CAIRO_RENDER_HAS_PICTURE_TRANSFORM(surface)	CAIRO_RENDER_AT_LEAST((surface), 0, 6)
 #define CAIRO_RENDER_HAS_FILTERS(surface)	CAIRO_RENDER_AT_LEAST((surface), 0, 6)
+#define CAIRO_RENDER_HAS_FILTER_GOOD(surface) FALSE
+#define CAIRO_RENDER_HAS_FILTER_BEST(surface) FALSE
 
 #define CAIRO_RENDER_HAS_EXTENDED_REPEAT(surface)	CAIRO_RENDER_AT_LEAST((surface), 0, 10)
 #define CAIRO_RENDER_HAS_GRADIENTS(surface)	CAIRO_RENDER_AT_LEAST((surface), 0, 10)
commit c8b1bf55ad016de0675f4e924fdb8e17051a029c
Author: Bill Spitzak <spitzak at gmail.com>
Date:   Thu Oct 9 19:46:11 2014 -0700

    image: Move filter decision to _cairo_pattern_analyze_filter
    
    The analysis to deterimine if the GOOD filter can be replaced with
    the BILINEAR filter is moved to this function so it can be used
    by backends other than the image backend.
    
    Reviewed-by: Bryce Harrington <b.harrington at samsung.com>

diff --git a/src/cairo-image-source.c b/src/cairo-image-source.c
index b6b6b9f..950053d 100644
--- a/src/cairo-image-source.c
+++ b/src/cairo-image-source.c
@@ -941,21 +941,17 @@ _pixman_image_set_properties (pixman_image_t *pixman_image,
 	    pixman_filter = PIXMAN_FILTER_FAST;
 	    break;
 	case CAIRO_FILTER_GOOD:
-	    pixman_filter = PIXMAN_FILTER_GOOD;
-	    if (dx > 1.35 || dy > 1.35) {
-		pixman_filter = PIXMAN_FILTER_SEPARABLE_CONVOLUTION;
-		kernel = KERNEL_BOX;
-		/* Clip the filter size to prevent extreme slowness. This
-		   value could be raised if 2-pass filtering is done */
-		if (dx > 16.0) dx = 16.0;
-		/* Match the bilinear filter for dimension scaling up: */
-		else if (dx < 1.0) dx = 1.0;
-		if (dy > 16.0) dy = 16.0;
-		else if (dy < 1.0) dy = 1.0;
-	    }
+	    pixman_filter = PIXMAN_FILTER_SEPARABLE_CONVOLUTION;
+	    kernel = KERNEL_BOX;
+	    /* Clip the filter size to prevent extreme slowness. This
+	       value could be raised if 2-pass filtering is done */
+	    if (dx > 16.0) dx = 16.0;
+	    if (dy > 16.0) dy = 16.0;
+	    /* Match the bilinear filter for scales > .75: */
+	    if (dx < 1.0/0.75) dx = 1.0;
+	    if (dy < 1.0/0.75) dy = 1.0;
 	    break;
 	case CAIRO_FILTER_BEST:
-	    pixman_filter = PIXMAN_FILTER_BEST;
 	    pixman_filter = PIXMAN_FILTER_SEPARABLE_CONVOLUTION;
 	    kernel = KERNEL_CATMULL_ROM; /* LANCZOS3 is better but not much */
 	    /* Clip the filter size to prevent extreme slowness. This
diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c
index e6fdae6..1a93d2b 100644
--- a/src/cairo-pattern.c
+++ b/src/cairo-pattern.c
@@ -3339,6 +3339,26 @@ _cairo_pattern_is_clear (const cairo_pattern_t *abstract_pattern)
 }
 
 /**
+ * Will given row of back-translation matrix work with bilinear scale?
+ * This is true for scales larger than 1. Also it was judged acceptable
+ * for scales larger than .75. And if there is integer translation
+ * then a scale of exactly .5 works.
+ */
+static int
+use_bilinear(double x, double y, double t)
+{
+    /* This is the inverse matrix! */
+    double h = x*x + y*y;
+    if (h < 1.0 / (0.75 * 0.75))
+	return TRUE; /* scale > .75 */
+    if ((h > 3.99 && h < 4.01) /* scale is 1/2 */
+	&& !_cairo_fixed_from_double(x*y) /* parallel to an axis */
+	&& _cairo_fixed_is_integer (_cairo_fixed_from_double (t)))
+	return TRUE;
+    return FALSE;
+}
+
+/**
  * _cairo_pattern_analyze_filter:
  * @pattern: surface pattern
  * @pad_out: location to store necessary padding in the source image, or %NULL
@@ -3378,7 +3398,21 @@ _cairo_pattern_analyze_filter (const cairo_pattern_t	*pattern,
 	     * more would be defensive...
 	     */
 	    pad = 0.5;
-	    optimized_filter = pattern->filter;
+	    /* Use BILINEAR for any scale greater than .75 instead
+	     * of GOOD. For scales of 1 and larger this is identical,
+	     * for the smaller sizes it was judged that the artifacts
+	     * were not worse than the artifacts from a box filer.
+	     * BILINEAR can also be used if the scale is exactly .5
+	     * and the translation in that direction is an integer.
+	     */
+	    if (pattern->filter == CAIRO_FILTER_GOOD &&
+		use_bilinear (pattern->matrix.xx, pattern->matrix.xy,
+			      pattern->matrix.x0) &&
+		use_bilinear (pattern->matrix.yx, pattern->matrix.yy,
+			      pattern->matrix.y0))
+		optimized_filter = CAIRO_FILTER_BILINEAR;
+	    else
+		optimized_filter = pattern->filter;
 	}
 	break;
 


More information about the cairo-commit mailing list