diff options
Diffstat (limited to 'core')
-rw-r--r-- | core/image.cpp | 151 | ||||
-rw-r--r-- | core/image.h | 1 | ||||
-rw-r--r-- | core/math/math_funcs.h | 6 | ||||
-rw-r--r-- | core/object.cpp | 12 |
4 files changed, 163 insertions, 7 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); diff --git a/core/image.h b/core/image.h index 69a42f169a..752ef20208 100644 --- a/core/image.h +++ b/core/image.h @@ -109,6 +109,7 @@ public: INTERPOLATE_BILINEAR, INTERPOLATE_CUBIC, INTERPOLATE_TRILINEAR, + INTERPOLATE_LANCZOS, /* INTERPOLATE_TRICUBIC, */ /* INTERPOLATE GAUSS */ }; diff --git a/core/math/math_funcs.h b/core/math/math_funcs.h index a75f2fb4ab..82b5b56c01 100644 --- a/core/math/math_funcs.h +++ b/core/math/math_funcs.h @@ -61,6 +61,12 @@ public: static _ALWAYS_INLINE_ double sinh(double p_x) { return ::sinh(p_x); } static _ALWAYS_INLINE_ float sinh(float p_x) { return ::sinhf(p_x); } + static _ALWAYS_INLINE_ float sinc(float p_x) { return p_x == 0 ? 1 : ::sin(p_x) / p_x; } + static _ALWAYS_INLINE_ double sinc(double p_x) { return p_x == 0 ? 1 : ::sin(p_x) / p_x; } + + static _ALWAYS_INLINE_ float sincn(float p_x) { return sinc(Math_PI * p_x); } + static _ALWAYS_INLINE_ double sincn(double p_x) { return sinc(Math_PI * p_x); } + static _ALWAYS_INLINE_ double cosh(double p_x) { return ::cosh(p_x); } static _ALWAYS_INLINE_ float cosh(float p_x) { return ::coshf(p_x); } diff --git a/core/object.cpp b/core/object.cpp index 937aa3c745..2a4ab93a6d 100644 --- a/core/object.cpp +++ b/core/object.cpp @@ -608,18 +608,16 @@ Variant Object::get_indexed(const Vector<StringName> &p_names, bool *r_valid) co } bool valid = false; - Variant current_value = get(p_names[0]); + Variant current_value = get(p_names[0], &valid); for (int i = 1; i < p_names.size(); i++) { current_value = current_value.get_named(p_names[i], &valid); - if (!valid) { - if (r_valid) - *r_valid = false; - return Variant(); - } + if (!valid) + break; } if (r_valid) - *r_valid = true; + *r_valid = valid; + return current_value; } |