diff options
Diffstat (limited to 'core/image.cpp')
-rw-r--r-- | core/image.cpp | 151 |
1 files changed, 151 insertions, 0 deletions
diff --git a/core/image.cpp b/core/image.cpp index 99d5eab864..30af724de9 100644 --- a/core/image.cpp +++ b/core/image.cpp @@ -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); |