Add support for high-quality scaling during video processing. This is enabled with the VA_FILTER_SCALING_HQ filter flag. The algorithm used for that is based on a Lanczos convolution kernel: 3 lobes on either side for luma samples, and 2 lobes on either side for chroma samples.
Signed-off-by: Gwenole Beauchesne <[email protected]> --- src/i965_vpp_avs.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/src/i965_vpp_avs.c b/src/i965_vpp_avs.c index 39407ac..33d6eba 100644 --- a/src/i965_vpp_avs.c +++ b/src/i965_vpp_avs.c @@ -52,6 +52,72 @@ avs_kernel_linear(float x) return abs_x < 1.0f ? 1 - abs_x : 0.0f; } +/* Convolution kernel for Lanczos-based interpolation */ +static float +avs_kernel_lanczos(float x, float a) +{ + const float abs_x = fabsf(x); + + if (abs_x == 0.0f) + return 1.0; + if (abs_x < a) + return a * sin(x * M_PI) * sin((x * M_PI) / a) / (M_PI * M_PI * x * x); + return 0.0f; +} + +/* Truncates floating-point value towards an epsilon factor */ +static inline float +avs_trunc_coeff(float x, float epsilon) +{ + return floorf(x / epsilon) * epsilon; +} + +/* Normalize coefficients for one sample/direction */ +static void +avs_normalize_coeffs_1(float *coeffs, int num_coeffs, float epsilon) +{ + float s, sum = 0.0; + int i, c, r, r1; + + for (i = 0; i < num_coeffs; i++) + sum += coeffs[i]; + + if (sum < epsilon) + return; + + s = 0.0; + for (i = 0; i < num_coeffs; i++) + s += (coeffs[i] = avs_trunc_coeff(coeffs[i] / sum, epsilon)); + + /* Distribute the remaining bits, while allocating more to the center */ + c = num_coeffs/2; + c = c - (coeffs[c - 1] > coeffs[c]); + + r = (1.0f - s) / epsilon; + r1 = r / 4; + if (coeffs[c + 1] != 0.0f) + coeffs[c] += r * epsilon; + else { + coeffs[c] += (r - 2*r1) * epsilon; + coeffs[c - 1] += r1 * epsilon; + coeffs[c + 1] += r1 * epsilon; + } +} + +/* Normalize all coefficients so that their sum yields 1.0f */ +static void +avs_normalize_coeffs(AVSCoeffs *coeffs, const AVSConfig *config) +{ + avs_normalize_coeffs_1(coeffs->y_k_h, config->num_luma_coeffs, + config->coeff_epsilon); + avs_normalize_coeffs_1(coeffs->y_k_v, config->num_luma_coeffs, + config->coeff_epsilon); + avs_normalize_coeffs_1(coeffs->uv_k_h, config->num_chroma_coeffs, + config->coeff_epsilon); + avs_normalize_coeffs_1(coeffs->uv_k_v, config->num_chroma_coeffs, + config->coeff_epsilon); +} + /* Generate coefficients for default quality (bilinear) */ static void avs_gen_coeffs_linear(float *coeffs, int num_coeffs, int phase, int num_phases, @@ -65,6 +131,21 @@ avs_gen_coeffs_linear(float *coeffs, int num_coeffs, int phase, int num_phases, coeffs[c + 1] = avs_kernel_linear(p - 1); } +/* Generate coefficients for high quality (lanczos) */ +static void +avs_gen_coeffs_lanczos(float *coeffs, int num_coeffs, int phase, int num_phases, + float f) +{ + const int c = num_coeffs/2 - 1; + const float p = (float)phase / (num_phases*2); + int i, l = 2; + + l = num_coeffs > 4 ? 3 : 2; + f = 1.0f / ceilf(1.0f/f); + for (i = 0; i < num_coeffs; i++) + coeffs[i] = avs_kernel_lanczos((i - (c + p)) * f, l); +} + /* Generate coefficients with the supplied scaler */ static void avs_gen_coeffs(AVSState *avs, float sx, float sy, AVSGenCoeffsFunc gen_coeffs) @@ -83,6 +164,8 @@ avs_gen_coeffs(AVSState *avs, float sx, float sy, AVSGenCoeffsFunc gen_coeffs) i, config->num_phases, sy); gen_coeffs(coeffs->uv_k_v, config->num_chroma_coeffs, i, config->num_phases, sy); + + avs_normalize_coeffs(coeffs, config); } } @@ -100,6 +183,9 @@ avs_update_coefficients(AVSState *avs, float sx, float sy, uint32_t flags) AVSGenCoeffsFunc gen_coeffs; switch (flags & VA_FILTER_SCALING_MASK) { + case VA_FILTER_SCALING_HQ: + gen_coeffs = avs_gen_coeffs_lanczos; + break; default: gen_coeffs = avs_gen_coeffs_linear; break; -- 1.9.1 _______________________________________________ Libva mailing list [email protected] http://lists.freedesktop.org/mailman/listinfo/libva
