|
@@ -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);
|