commit 40847798271f9b8986cf90560dd1438fd8772847 Author: Owen W. Taylor Date: Sat Apr 5 11:34:27 2008 -0400 Clean up handling of filter radius; optimize filter when possible cairo-pattern.c: - Factor out filter analysis code from _cairo_pattern_get_extents() and use it in _cairo_pattern_acquire_surface_for_surface() as well. - Optimize the filter to NEAREST when the matrix takes source pixels exactly onto destination pixels cairo-matrix.c cairoint.h: Add _cairo_matrix_is_pixel_exact() cairo-svg-surface.c cairo-win32-printing-surface.c: Remove instances of calling _cairo_pattern_acquire_surface() on a known-surface-pattern with a hack to say "give me the entire surface". If you know you just want the entire surface as an image surface, that can be done more simply. diff --git a/src/cairo-matrix.c b/src/cairo-matrix.c index d10d156..7262d90 100644 --- a/src/cairo-matrix.c +++ b/src/cairo-matrix.c @@ -600,6 +600,36 @@ _cairo_matrix_is_integer_translation (const cairo_matrix_t *matrix, return FALSE; } +/* By pixel exact here, we mean a matrix that is composed only of + * 90 degree rotations, flips, and and integer translations. If + * we transform an image with a pixel-exact matrix, filtering + * is not useful. + */ +cairo_private cairo_bool_t +_cairo_matrix_is_pixel_exact(const cairo_matrix_t *matrix) +{ + cairo_fixed_t x0_fixed, y0_fixed; + + if (matrix->xy == 0.0 && matrix->yx == 0.0) { + if (!(matrix->xx == 1.0 || matrix->xx == -1.0)) + return FALSE; + if (!(matrix->yy == 1.0 || matrix->yy == -1.0)) + return FALSE; + } else if (matrix->xx == 0.0 && matrix->yy == 0.0) { + if (!(matrix->xy == 1.0 || matrix->xy == -1.0)) + return FALSE; + if (!(matrix->yx == 1.0 || matrix->yx == -1.0)) + return FALSE; + } else { + return FALSE; + } + + x0_fixed = _cairo_fixed_from_double (matrix->x0); + y0_fixed = _cairo_fixed_from_double (matrix->y0); + + return _cairo_fixed_is_integer (x0_fixed) && _cairo_fixed_is_integer (y0_fixed); +} + /* A circle in user space is transformed into an ellipse in device space. diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c index e1d4b5c..114b410 100644 --- a/src/cairo-pattern.c +++ b/src/cairo-pattern.c @@ -1550,6 +1550,63 @@ _cairo_pattern_is_opaque (const cairo_pattern_t *abstract_pattern) return FALSE; } +/** + * 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) + * + * pattern: surface pattern + * pad_out: location to store necessary padding in the source image, or %NULL + * optimized_filter_out: location to store the filter to use, or %NULL + */ +static void +_cairo_pattern_analyze_filter(cairo_surface_pattern_t *pattern, + double *pad_out, + cairo_filter_t *optimized_filter_out) +{ + double pad; + cairo_filter_t optimized_filter; + + switch (pattern->base.filter) { + case CAIRO_FILTER_GOOD: + case CAIRO_FILTER_BEST: + case CAIRO_FILTER_BILINEAR: + /* If source pixels map 1:1 onto destination pixels, we do + * not need to filter (and do not want to filter, since it + * will cause blurriness) + */ + if (_cairo_matrix_is_pixel_exact(&pattern->base.matrix)) { + pad = 0.; + optimized_filter = 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; + optimized_filter = pattern->base.filter; + } + break; + case CAIRO_FILTER_FAST: + case CAIRO_FILTER_NEAREST: + case CAIRO_FILTER_GAUSSIAN: + default: + pad = 0; + optimized_filter = pattern->base.filter; + break; + } + + if (pad_out) + *pad_out = pad; + if (optimized_filter_out) + *optimized_filter_out = optimized_filter; +} + static cairo_int_status_t _cairo_pattern_acquire_surface_for_surface (cairo_surface_pattern_t *pattern, cairo_surface_t *dst, @@ -1562,18 +1619,17 @@ _cairo_pattern_acquire_surface_for_surface (cairo_surface_pattern_t *pattern, { cairo_int_status_t status; int tx, ty; + double pad; attr->acquired = FALSE; attr->extend = pattern->base.extend; - attr->filter = pattern->base.filter; if (_cairo_matrix_is_integer_translation (&pattern->base.matrix, &tx, &ty)) { cairo_matrix_init_identity (&attr->matrix); attr->x_offset = tx; attr->y_offset = ty; - attr->filter = CAIRO_FILTER_NEAREST; } else { @@ -1583,6 +1639,8 @@ _cairo_pattern_acquire_surface_for_surface (cairo_surface_pattern_t *pattern, ty = 0; } + _cairo_pattern_analyze_filter(pattern, &pad, &attr->filter); + /* XXX: Hack: * * The way we currently support CAIRO_EXTEND_REFLECT is to create @@ -1677,10 +1735,7 @@ _cairo_pattern_acquire_surface_for_surface (cairo_surface_pattern_t *pattern, return status; /* If we're repeating, we just play it safe and clone the entire surface. */ - /* If requested width and height are -1, clone the entire surface. - * This is relied on in the svg backend. */ - if (attr->extend == CAIRO_EXTEND_REPEAT || - (width == (unsigned int) -1 && height == (unsigned int) -1)) { + if (attr->extend == CAIRO_EXTEND_REPEAT) { x = extents.x; y = extents.y; width = extents.width; @@ -1695,26 +1750,21 @@ _cairo_pattern_acquire_surface_for_surface (cairo_surface_pattern_t *pattern, double y1 = y; double x2 = x + width; double y2 = y + height; - cairo_bool_t is_tight; _cairo_matrix_transform_bounding_box (&attr->matrix, &x1, &y1, &x2, &y2, - &is_tight); + NULL); /* The transform_bounding_box call may have resulted * in a region larger than the surface, but we never * want to clone more than the surface itself, (we * know we're not repeating at this point due to the * above. - * - * XXX: The one padding here is to account for filter - * radius. It's a workaround right now, until we get a - * proper fix. (see bug #10508) */ - x = MAX (0, floor (x1) - 1); - y = MAX (0, floor (y1) - 1); - width = MIN (extents.width, ceil (x2) + 1) - x; - height = MIN (extents.height, ceil (y2) + 1) - y; + x = MAX (0, floor (x1 - pad)); + y = MAX (0, floor (y1 - pad)); + width = MIN (extents.width, ceil (x2 + pad)) - x; + height = MIN (extents.height, ceil (y2 + pad)) - y; } x += tx; y += ty; @@ -1742,6 +1792,9 @@ _cairo_pattern_acquire_surface_for_surface (cairo_surface_pattern_t *pattern, * A convenience function to obtain a surface to use as the source for * drawing on @dst. * + * Note that this function is only suitable for use when the destination + * surface is pixel based and 1 device unit maps to one pixel. + * * Return value: %CAIRO_STATUS_SUCCESS if a surface was stored in @surface_out. **/ cairo_int_status_t @@ -1981,37 +2034,18 @@ _cairo_pattern_get_extents (cairo_pattern_t *pattern, cairo_surface_t *surface = surface_pattern->surface; cairo_matrix_t imatrix; double x1, y1, x2, y2; + double pad; status = _cairo_surface_get_extents (surface, &surface_extents); if (status) return status; - x1 = surface_extents.x; - y1 = surface_extents.y; - x2 = x1 + surface_extents.width; - y2 = y1 + surface_extents.height; - - /* The filter can effectively enlarge the extents of the - * pattern, so extend as necessary. Note: We aren't doing any - * backend-specific querying of filter box sizes at this time, - * (since currently no specific backends that could do custom - * filters are calling _cairo_pattern_get_extents). */ - switch (pattern->filter) { - case CAIRO_FILTER_GOOD: - case CAIRO_FILTER_BEST: - case CAIRO_FILTER_BILINEAR: - x1 -= 0.5; - y1 -= 0.5; - x2 += 0.5; - y2 += 0.5; - break; - case CAIRO_FILTER_FAST: - case CAIRO_FILTER_NEAREST: - case CAIRO_FILTER_GAUSSIAN: - default: - /* Nothing to do */ - break; - } + _cairo_pattern_analyze_filter(surface_pattern, &pad, NULL); + + x1 = surface_extents.x - pad; + y1 = surface_extents.y - pad; + x2 = surface_extents.x + surface_extents.width + pad; + y2 = surface_extents.y + surface_extents.height + pad; imatrix = pattern->matrix; status = cairo_matrix_invert (&imatrix); diff --git a/src/cairo-svg-surface.c b/src/cairo-svg-surface.c index a900c24..106671c 100644 --- a/src/cairo-svg-surface.c +++ b/src/cairo-svg-surface.c @@ -965,23 +965,15 @@ _cairo_svg_surface_emit_composite_image_pattern (cairo_output_stream_t *output const cairo_matrix_t *parent_matrix, const char *extra_attributes) { - cairo_surface_t *surface; cairo_surface_attributes_t surface_attr; cairo_rectangle_int_t extents; cairo_status_t status; cairo_matrix_t p2u; - status = _cairo_pattern_acquire_surface ((cairo_pattern_t *)pattern, - (cairo_surface_t *)svg_surface, - 0, 0, (unsigned int)-1, (unsigned int)-1, - &surface, &surface_attr); + status = _cairo_surface_get_extents (pattern->surface, &extents); if (status) return status; - status = _cairo_surface_get_extents (surface, &extents); - if (status) - goto FAIL; - p2u = pattern->base.matrix; status = cairo_matrix_invert (&p2u); /* cairo_pattern_set_matrix ensures the matrix is invertible */ @@ -1010,17 +1002,13 @@ _cairo_svg_surface_emit_composite_image_pattern (cairo_output_stream_t *output _cairo_output_stream_printf (output, " xlink:href=\""); - status = _cairo_surface_base64_encode (surface, output); + status = _cairo_surface_base64_encode (pattern->surface, output); _cairo_output_stream_printf (output, "\"/>\n"); if (pattern_id != invalid_pattern_id) _cairo_output_stream_printf (output, "\n"); - FAIL: - _cairo_pattern_release_surface ((cairo_pattern_t *)pattern, - surface, &surface_attr); - return status; } diff --git a/src/cairo-win32-printing-surface.c b/src/cairo-win32-printing-surface.c index cc3a17b..13c3d22 100644 --- a/src/cairo-win32-printing-surface.c +++ b/src/cairo-win32-printing-surface.c @@ -489,7 +489,6 @@ _cairo_win32_printing_surface_paint_image_pattern (cairo_win32_surface_t *surf { cairo_status_t status; cairo_extend_t extend; - cairo_surface_t *pat_surface; cairo_surface_attributes_t pat_attr; cairo_image_surface_t *image; void *image_extra; @@ -516,17 +515,11 @@ _cairo_win32_printing_surface_paint_image_pattern (cairo_win32_surface_t *surf background_color = CAIRO_COLOR_BLACK; extend = cairo_pattern_get_extend (&pattern->base); - status = _cairo_pattern_acquire_surface ((cairo_pattern_t *)pattern, - (cairo_surface_t *)surface, - 0, 0, -1, -1, - &pat_surface, &pat_attr); + + status = _cairo_surface_acquire_source_image (pattern->surface, &image, &image_extra); if (status) return status; - status = _cairo_surface_acquire_source_image (pat_surface, &image, &image_extra); - if (status) - goto FINISH; - if (image->base.status) { status = image->base.status; goto FINISH2; @@ -643,9 +636,7 @@ FINISH3: if (opaque_image != image) cairo_surface_destroy (opaque_surface); FINISH2: - _cairo_surface_release_source_image (pat_surface, image, image_extra); -FINISH: - _cairo_pattern_release_surface ((cairo_pattern_t *)pattern, pat_surface, &pat_attr); + _cairo_surface_release_source_image (pattern->surface, image, image_extra); return status; } diff --git a/src/cairoint.h b/src/cairoint.h index 01ad567..768e6b3 100644 --- a/src/cairoint.h +++ b/src/cairoint.h @@ -2051,6 +2051,9 @@ cairo_private cairo_bool_t _cairo_matrix_is_integer_translation(const cairo_matrix_t *matrix, int *itx, int *ity); +cairo_private cairo_bool_t +_cairo_matrix_is_pixel_exact(const cairo_matrix_t *matrix); + cairo_private double _cairo_matrix_transformed_circle_major_axis(cairo_matrix_t *matrix, double radius);