Implement Lanczos image filter

This commit is contained in:
Daw11 2019-05-05 14:03:52 +02:00
parent d8617f237a
commit 28bff3d1ad
5 changed files with 161 additions and 2 deletions

View file

@ -725,6 +725,131 @@ static void _scale_nearest(const uint8_t *__restrict p_src, uint8_t *__restrict
}
}
#define LANCZOS_TYPE 3
static float _lanczos(float p_x) {
return Math::abs(p_x) >= LANCZOS_TYPE ? 0 : Math::sincn(p_x) * Math::sincn(p_x / LANCZOS_TYPE);
}
template <int CC, class T>
static void _scale_lanczos(const uint8_t *__restrict p_src, uint8_t *__restrict p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) {
int32_t src_width = p_src_width;
int32_t src_height = p_src_height;
int32_t dst_height = p_dst_height;
int32_t dst_width = p_dst_width;
uint32_t buffer_size = src_height * dst_width * CC;
float *buffer = memnew_arr(float, buffer_size); // Store the first pass in a buffer
{ // FIRST PASS (horizontal)
float x_scale = float(src_width) / float(dst_width);
float scale_factor = MAX(x_scale, 1); // A larger kernel is required only when downscaling
int32_t half_kernel = LANCZOS_TYPE * scale_factor;
float *kernel = memnew_arr(float, half_kernel * 2 - 1);
for (int32_t buffer_x = 0; buffer_x < dst_width; buffer_x++) {
float src_real_x = buffer_x * x_scale;
int32_t src_x = src_real_x;
int32_t start_x = MAX(0, src_x - half_kernel + 1);
int32_t end_x = MIN(src_width - 1, src_x + half_kernel);
// Create the kernel used by all the pixels of the column
for (int32_t target_x = start_x; target_x <= end_x; target_x++)
kernel[target_x - start_x] = _lanczos((src_real_x - target_x) / scale_factor);
for (int32_t buffer_y = 0; buffer_y < src_height; buffer_y++) {
float pixel[CC] = { 0 };
float weight = 0;
for (int32_t target_x = start_x; target_x <= end_x; target_x++) {
float lanczos_val = kernel[target_x - start_x];
weight += lanczos_val;
const T *__restrict src_data = ((const T *)p_src) + (buffer_y * src_width + target_x) * CC;
for (uint32_t i = 0; i < CC; i++) {
if (sizeof(T) == 2) //half float
pixel[i] += Math::half_to_float(src_data[i]) * lanczos_val;
else
pixel[i] += src_data[i] * lanczos_val;
}
}
float *dst_data = ((float *)buffer) + (buffer_y * dst_width + buffer_x) * CC;
for (uint32_t i = 0; i < CC; i++)
dst_data[i] = pixel[i] / weight; // Normalize the sum of all the samples
}
}
memdelete_arr(kernel);
} // End of first pass
{ // SECOND PASS (vertical + result)
float y_scale = float(src_height) / float(dst_height);
float scale_factor = MAX(y_scale, 1);
int32_t half_kernel = LANCZOS_TYPE * scale_factor;
float *kernel = memnew_arr(float, half_kernel * 2 - 1);
for (int32_t dst_y = 0; dst_y < dst_height; dst_y++) {
float buffer_real_y = dst_y * y_scale;
int32_t buffer_y = buffer_real_y;
int32_t start_y = MAX(0, buffer_y - half_kernel + 1);
int32_t end_y = MIN(src_height - 1, buffer_y + half_kernel);
for (int32_t target_y = start_y; target_y <= end_y; target_y++)
kernel[target_y - start_y] = _lanczos((buffer_real_y - target_y) / scale_factor);
for (int32_t dst_x = 0; dst_x < dst_width; dst_x++) {
float pixel[CC] = { 0 };
float weight = 0;
for (int32_t target_y = start_y; target_y <= end_y; target_y++) {
float lanczos_val = kernel[target_y - start_y];
weight += lanczos_val;
float *buffer_data = ((float *)buffer) + (target_y * dst_width + dst_x) * CC;
for (uint32_t i = 0; i < CC; i++)
pixel[i] += buffer_data[i] * lanczos_val;
}
T *dst_data = ((T *)p_dst) + (dst_y * dst_width + dst_x) * CC;
for (uint32_t i = 0; i < CC; i++) {
pixel[i] /= weight;
if (sizeof(T) == 1) //byte
dst_data[i] = CLAMP(Math::fast_ftoi(pixel[i]), 0, 255);
else if (sizeof(T) == 2) //half float
dst_data[i] = Math::make_half_float(pixel[i]);
else // float
dst_data[i] = pixel[i];
}
}
}
memdelete_arr(kernel);
} // End of second pass
memdelete_arr(buffer);
}
static void _overlay(const uint8_t *__restrict p_src, uint8_t *__restrict p_dst, float p_alpha, uint32_t p_width, uint32_t p_height, uint32_t p_pixel_size) {
uint16_t alpha = CLAMP((uint16_t)(p_alpha * 256.0f), 0, 256);
@ -939,6 +1064,31 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) {
}
}
} break;
case INTERPOLATE_LANCZOS: {
if (format >= FORMAT_L8 && format <= FORMAT_RGBA8) {
switch (get_format_pixel_size(format)) {
case 1: _scale_lanczos<1, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height); break;
case 2: _scale_lanczos<2, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height); break;
case 3: _scale_lanczos<3, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height); break;
case 4: _scale_lanczos<4, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height); break;
}
} else if (format >= FORMAT_RF && format <= FORMAT_RGBAF) {
switch (get_format_pixel_size(format)) {
case 4: _scale_lanczos<1, float>(r_ptr, w_ptr, width, height, p_width, p_height); break;
case 8: _scale_lanczos<2, float>(r_ptr, w_ptr, width, height, p_width, p_height); break;
case 12: _scale_lanczos<3, float>(r_ptr, w_ptr, width, height, p_width, p_height); break;
case 16: _scale_lanczos<4, float>(r_ptr, w_ptr, width, height, p_width, p_height); break;
}
} else if (format >= FORMAT_RH && format <= FORMAT_RGBAH) {
switch (get_format_pixel_size(format)) {
case 2: _scale_lanczos<1, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height); break;
case 4: _scale_lanczos<2, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height); break;
case 6: _scale_lanczos<3, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height); break;
case 8: _scale_lanczos<4, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height); break;
}
}
} break;
}
r = PoolVector<uint8_t>::Read();
@ -2685,6 +2835,7 @@ void Image::_bind_methods() {
BIND_ENUM_CONSTANT(INTERPOLATE_BILINEAR);
BIND_ENUM_CONSTANT(INTERPOLATE_CUBIC);
BIND_ENUM_CONSTANT(INTERPOLATE_TRILINEAR);
BIND_ENUM_CONSTANT(INTERPOLATE_LANCZOS);
BIND_ENUM_CONSTANT(ALPHA_NONE);
BIND_ENUM_CONSTANT(ALPHA_BIT);