diff options
-rw-r--r-- | src/cairo-composite-rectangles.c | 2 | ||||
-rw-r--r-- | src/cairo-pattern-private.h | 5 | ||||
-rw-r--r-- | src/cairo-pattern.c | 275 | ||||
-rw-r--r-- | src/cairo-xlib-core-compositor.c | 4 | ||||
-rw-r--r-- | src/cairoint.h | 4 | ||||
-rw-r--r-- | src/drm/cairo-drm-i915-shader.c | 39 |
6 files changed, 176 insertions, 153 deletions
diff --git a/src/cairo-composite-rectangles.c b/src/cairo-composite-rectangles.c index e6639d0c3..6c3e97d4a 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 bbcfadd85..be8ab9fc2 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 1a93d2ba5..e16823dcc 100644 --- a/src/cairo-pattern.c +++ b/src/cairo-pattern.c @@ -28,8 +28,6 @@ * Carl Worth <cworth@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,@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 9398079b4..5babcc81b 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 75b34d00c..b4e8ac87f 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 a1911d0a4..85aa98433 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) { |