GOOD uses the BOX filter, and uses BILINEAR for all scales > 1/1.35. BEST uses CATMULL_ROM and fiddles with the filter scale so that it produces square pixels at large zoom in, but normal filtering between 1 and 2. --- pixman/pixman-image.c | 267 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 191 insertions(+), 76 deletions(-)
diff --git a/pixman/pixman-image.c b/pixman/pixman-image.c index 1ff1a49..2a312b1 100644 --- a/pixman/pixman-image.c +++ b/pixman/pixman-image.c @@ -28,6 +28,7 @@ #include <stdio.h> #include <string.h> #include <assert.h> +#include <math.h> #include "pixman-private.h" @@ -261,6 +262,48 @@ pixman_disable_out_of_bounds_workaround (void) { } +static int +nearest_works (pixman_fixed_t (*m)[3], uint32_t flags) +{ + /* Here we have a chance to optimize BILINEAR filter to NEAREST if + * they are equivalent for the currently used transformation matrix. + */ + if ( + /* affine and integer translation components in matrix ... */ + ((flags & FAST_PATH_AFFINE_TRANSFORM) && + !pixman_fixed_frac (m[0][2] | + m[1][2])) && + ( + /* ... combined with a simple rotation */ + (flags & (FAST_PATH_ROTATE_90_TRANSFORM | + FAST_PATH_ROTATE_180_TRANSFORM | + FAST_PATH_ROTATE_270_TRANSFORM)) || + /* ... or combined with a simple non-rotated translation */ + (m[0][0] == pixman_fixed_1 && + m[1][1] == pixman_fixed_1 && + m[0][1] == 0 && + m[1][0] == 0) + ) + ) + { + /* FIXME: there are some affine-test failures, showing that + * handling of BILINEAR and NEAREST filter is not quite + * equivalent when getting close to 32K for the translation + * components of the matrix. That's likely some bug, but for + * now just skip BILINEAR->NEAREST optimization in this case. + */ + pixman_fixed_t magic_limit = pixman_int_to_fixed (30000); + if (m[0][2] <= magic_limit && + m[1][2] <= magic_limit && + m[0][2] >= -magic_limit && + m[1][2] >= -magic_limit) + { + return 1; + } + } + return 0; +} + static void compute_image_info (pixman_image_t *image) { @@ -274,112 +317,184 @@ compute_image_info (pixman_image_t *image) FAST_PATH_X_UNIT_POSITIVE | FAST_PATH_Y_UNIT_ZERO | FAST_PATH_AFFINE_TRANSFORM); + switch (image->common.filter) + { + case PIXMAN_FILTER_CONVOLUTION: + break; + case PIXMAN_FILTER_SEPARABLE_CONVOLUTION: + flags |= FAST_PATH_SEPARABLE_CONVOLUTION_FILTER; + break; + default: + flags |= (FAST_PATH_NEAREST_FILTER | + FAST_PATH_NO_CONVOLUTION_FILTER); + break; + } } else { + pixman_fixed_t (*m)[3] = image->common.transform->matrix; + double dx, dy; + int xsubsample, ysubsample; + flags |= FAST_PATH_HAS_TRANSFORM; - if (image->common.transform->matrix[2][0] == 0 && - image->common.transform->matrix[2][1] == 0 && - image->common.transform->matrix[2][2] == pixman_fixed_1) + if (m[2][0] == 0 && + m[2][1] == 0 && + m[2][2] == pixman_fixed_1) { flags |= FAST_PATH_AFFINE_TRANSFORM; - if (image->common.transform->matrix[0][1] == 0 && - image->common.transform->matrix[1][0] == 0) + if (m[0][1] == 0 && + m[1][0] == 0) { - if (image->common.transform->matrix[0][0] == -pixman_fixed_1 && - image->common.transform->matrix[1][1] == -pixman_fixed_1) + if (m[0][0] == -pixman_fixed_1 && + m[1][1] == -pixman_fixed_1) { flags |= FAST_PATH_ROTATE_180_TRANSFORM; } flags |= FAST_PATH_SCALE_TRANSFORM; } - else if (image->common.transform->matrix[0][0] == 0 && - image->common.transform->matrix[1][1] == 0) + else if (m[0][0] == 0 && + m[1][1] == 0) { - pixman_fixed_t m01 = image->common.transform->matrix[0][1]; - pixman_fixed_t m10 = image->common.transform->matrix[1][0]; - - if (m01 == -pixman_fixed_1 && m10 == pixman_fixed_1) + if (m[0][1] == -pixman_fixed_1 && + m[1][0] == pixman_fixed_1) flags |= FAST_PATH_ROTATE_90_TRANSFORM; - else if (m01 == pixman_fixed_1 && m10 == -pixman_fixed_1) + else if (m[0][1] == pixman_fixed_1 && + m[1][0] == -pixman_fixed_1) flags |= FAST_PATH_ROTATE_270_TRANSFORM; } } - if (image->common.transform->matrix[0][0] > 0) + if (m[0][0] > 0) flags |= FAST_PATH_X_UNIT_POSITIVE; - if (image->common.transform->matrix[1][0] == 0) + if (m[1][0] == 0) flags |= FAST_PATH_Y_UNIT_ZERO; - } - /* Filter */ - switch (image->common.filter) - { - case PIXMAN_FILTER_NEAREST: - case PIXMAN_FILTER_FAST: - flags |= (FAST_PATH_NEAREST_FILTER | FAST_PATH_NO_CONVOLUTION_FILTER); - break; + switch (image->common.filter) + { + case PIXMAN_FILTER_NEAREST: + case PIXMAN_FILTER_FAST: + flags |= (FAST_PATH_NEAREST_FILTER | + FAST_PATH_NO_CONVOLUTION_FILTER); + break; - case PIXMAN_FILTER_BILINEAR: - case PIXMAN_FILTER_GOOD: - case PIXMAN_FILTER_BEST: - flags |= (FAST_PATH_BILINEAR_FILTER | FAST_PATH_NO_CONVOLUTION_FILTER); + case PIXMAN_FILTER_BILINEAR: + if (nearest_works(m, flags)) + flags |= (FAST_PATH_NEAREST_FILTER | + FAST_PATH_NO_CONVOLUTION_FILTER); + else + flags |= (FAST_PATH_BILINEAR_FILTER | + FAST_PATH_NO_CONVOLUTION_FILTER); + break; - /* Here we have a chance to optimize BILINEAR filter to NEAREST if - * they are equivalent for the currently used transformation matrix. - */ - if (flags & FAST_PATH_ID_TRANSFORM) - { - flags |= FAST_PATH_NEAREST_FILTER; - } - else if ( - /* affine and integer translation components in matrix ... */ - ((flags & FAST_PATH_AFFINE_TRANSFORM) && - !pixman_fixed_frac (image->common.transform->matrix[0][2] | - image->common.transform->matrix[1][2])) && - ( - /* ... combined with a simple rotation */ - (flags & (FAST_PATH_ROTATE_90_TRANSFORM | - FAST_PATH_ROTATE_180_TRANSFORM | - FAST_PATH_ROTATE_270_TRANSFORM)) || - /* ... or combined with a simple non-rotated translation */ - (image->common.transform->matrix[0][0] == pixman_fixed_1 && - image->common.transform->matrix[1][1] == pixman_fixed_1 && - image->common.transform->matrix[0][1] == 0 && - image->common.transform->matrix[1][0] == 0) - ) - ) - { - /* FIXME: there are some affine-test failures, showing that - * handling of BILINEAR and NEAREST filter is not quite - * equivalent when getting close to 32K for the translation - * components of the matrix. That's likely some bug, but for - * now just skip BILINEAR->NEAREST optimization in this case. - */ - pixman_fixed_t magic_limit = pixman_int_to_fixed (30000); - if (image->common.transform->matrix[0][2] <= magic_limit && - image->common.transform->matrix[1][2] <= magic_limit && - image->common.transform->matrix[0][2] >= -magic_limit && - image->common.transform->matrix[1][2] >= -magic_limit) - { - flags |= FAST_PATH_NEAREST_FILTER; + case PIXMAN_FILTER_GOOD: + if (nearest_works(m, flags)) { + flags |= (FAST_PATH_NEAREST_FILTER | + FAST_PATH_NO_CONVOLUTION_FILTER); + break; } - } - break; + /* Compute derivative at 1,1. Note that division by last row + both contributes the w component and converts from fixed. */ + dy = 1.0 / (m[2][0] + m[2][1] + m[2][2]); + dx = hypot (m[0][0] * dy, m[0][1] * dy); + dy = hypot (m[1][0] * dy, m[1][1] * dy); + /* clamp at 1, and use bilinear for scales near 1 that don't + look any better with the box filter. Also clamp off large + filters to prevent extreme slowness. */ + if (dx <= 1.35) { + if (dy <= 1.35) { + flags |= (FAST_PATH_BILINEAR_FILTER | + FAST_PATH_NO_CONVOLUTION_FILTER); + break; + } + dx = 1.0; + } else if (dx > 16.0) + dx = 16.0; + if (dy <= 1.35) + dy = 1.0; + else if (dy > 16.0) + dy = 16.0; + + xsubsample = 0; + while (dx * (1 << xsubsample) <= 128.0) xsubsample++; + ysubsample = 0; + while (dy * (1 << ysubsample) <= 128.0) ysubsample++; + + if (image->common.filter_params) + free (image->common.filter_params); + + image->common.filter_params = + pixman_filter_create_separable_convolution + ( & image->common.n_filter_params, + pixman_double_to_fixed(dx), + pixman_double_to_fixed(dy), + 0, 0, + PIXMAN_KERNEL_BOX, PIXMAN_KERNEL_BOX, + xsubsample, ysubsample); + + flags |= FAST_PATH_SEPARABLE_CONVOLUTION_FILTER; + break; - case PIXMAN_FILTER_CONVOLUTION: - break; + case PIXMAN_FILTER_BEST: + if (nearest_works(m, flags)) { + flags |= (FAST_PATH_NEAREST_FILTER | + FAST_PATH_NO_CONVOLUTION_FILTER); + break; + } + /* Compute derivative at 1,1. Note that division by last row + both contributes the w component and converts from fixed. */ + dy = 1.0 / (m[2][0] + m[2][1] + m[2][2]); + dx = hypot (m[0][0] * dy, m[0][1] * dy); + dy = hypot (m[1][0] * dy, m[1][1] * dy); + + /* Clamp to prevent extreme slowness (it also switches to box). + When zooming in, blur until pixels are 2x, then use square + pixels for further zoom. Add more blur at extreme zoom-in + to avoid lots of subsamples */ + if (dx > 24.0) dx = 24.0; + else if (dx < 1.0) { + if (dx >= 0.5) dx = 1.0; + else if (dx > 1.0/128) dx = 1.0 / (1.0 / dx - 1.0); + else dx = 1.0/127; + } + if (dy > 24.0) dy = 24.0; + else if (dy < 1.0) { + if (dy >= 0.5) dy = 1.0; + else if (dy > 1.0/128) dy = 1.0 / (1.0 / dy - 1.0); + else dy = 1.0/127; + } - case PIXMAN_FILTER_SEPARABLE_CONVOLUTION: - flags |= FAST_PATH_SEPARABLE_CONVOLUTION_FILTER; - break; + xsubsample = 0; + while (dx * (1 << xsubsample) <= 128.0) xsubsample++; + ysubsample = 0; + while (dy * (1 << ysubsample) <= 128.0) ysubsample++; + + image->common.filter_params = + pixman_filter_create_separable_convolution + ( & image->common.n_filter_params, + pixman_double_to_fixed(dx), + pixman_double_to_fixed(dy), + 0, 0, + dx < 16.0 ? PIXMAN_KERNEL_CATMULL_ROM : PIXMAN_KERNEL_BOX, + dy < 16.0 ? PIXMAN_KERNEL_CATMULL_ROM : PIXMAN_KERNEL_BOX, + xsubsample, ysubsample); + + flags |= FAST_PATH_SEPARABLE_CONVOLUTION_FILTER; + break; - default: - flags |= FAST_PATH_NO_CONVOLUTION_FILTER; - break; + case PIXMAN_FILTER_CONVOLUTION: + break; + + case PIXMAN_FILTER_SEPARABLE_CONVOLUTION: + flags |= FAST_PATH_SEPARABLE_CONVOLUTION_FILTER; + break; + + default: + flags |= FAST_PATH_NO_CONVOLUTION_FILTER; + break; + } } /* Repeat mode */ -- 1.7.9.5 _______________________________________________ Pixman mailing list Pixman@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/pixman