From a4be6be15db707877031756ed54cfa91b133a610 Mon Sep 17 00:00:00 2001 From: Aras Pranckevicius Date: Fri, 29 Dec 2023 10:39:39 +0200 Subject: [PATCH 1/2] ImBuf: simplify TransformUserData init math Adding X and then subtracting X, or multiplying by Y and then dividing by Y do kinda cancel out, yo. --- source/blender/imbuf/intern/transform.cc | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/source/blender/imbuf/intern/transform.cc b/source/blender/imbuf/intern/transform.cc index 833d8ef52f7..c434041b84f 100644 --- a/source/blender/imbuf/intern/transform.cc +++ b/source/blender/imbuf/intern/transform.cc @@ -81,16 +81,12 @@ struct TransformUserData { void init_add_x(const float4x4 &transform_matrix) { - const double width = src->x; - add_x = double2(transform_matrix.x_axis()) * width + double2(transform_matrix.location()); - add_x = (add_x - start_uv) * (1.0 / width); + add_x = double2(transform_matrix.x_axis()); } void init_add_y(const float4x4 &transform_matrix) { - const double height = src->y; - add_y = double2(transform_matrix.y_axis()) * height + double2(transform_matrix.location()); - add_y = (add_y - start_uv) * (1.0 / height); + add_y = double2(transform_matrix.y_axis()); } void init_subsampling(const int num_subsamples) -- 2.30.2 From ba8e0e0ea2a8e06cac0b8354fba6282b8c1a7e04 Mon Sep 17 00:00:00 2001 From: Aras Pranckevicius Date: Fri, 29 Dec 2023 13:25:07 +0200 Subject: [PATCH 2/2] ImBuf: fix off by half a pixel issues in IMB_transform IMB_transform code was not properly doing mapping between pixel and texel space. This caused for example bilinear with 0.5 scale not properly averaging each 2x2 source image pixel, but rather just picking one out of 2x2 source pixels. The subsampling case setup code had a sign error too. --- source/blender/imbuf/intern/transform.cc | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/source/blender/imbuf/intern/transform.cc b/source/blender/imbuf/intern/transform.cc index c434041b84f..95775a2ad76 100644 --- a/source/blender/imbuf/intern/transform.cc +++ b/source/blender/imbuf/intern/transform.cc @@ -98,7 +98,7 @@ struct TransformUserData { for (int y : IndexRange(0, num_subsamples)) { for (int x : IndexRange(0, num_subsamples)) { - double2 delta_uv = -offset_x - offset_y; + double2 delta_uv = offset_x + offset_y; delta_uv += x * subsample_add_x; delta_uv += y * subsample_add_y; subsampling.delta_uvs.append(delta_uv); @@ -309,6 +309,13 @@ class Sampler { u = wrap_uv(u, source->x); v = wrap_uv(v, source->y); } + /* BLI_bilinear_interpolation functions use `floor(uv)` and `floor(uv)+1` + * texels. For proper mapping between pixel and texel spaces, need to + * subtract 0.5. */ + if constexpr (Filter == IMB_FILTER_BILINEAR) { + u -= 0.5f; + v -= 0.5f; + } if constexpr (Filter == IMB_FILTER_BILINEAR && std::is_same_v && NumChannels == 4) { @@ -501,9 +508,10 @@ class ScanlineProcessor { private: void process_one_sample_per_pixel(const TransformUserData *user_data, int scanline) { - double2 uv = user_data->start_uv + - user_data->destination_region.x_range.first() * user_data->add_x + - user_data->add_y * scanline; + /* Note: sample at pixel center for proper filtering. */ + double pixel_x = user_data->destination_region.x_range.first() + 0.5; + double pixel_y = scanline + 0.5; + double2 uv = user_data->start_uv + user_data->add_x * pixel_x + user_data->add_y * pixel_y; output.init_pixel_pointer(user_data->dst, int2(user_data->destination_region.x_range.first(), scanline)); @@ -522,9 +530,10 @@ class ScanlineProcessor { void process_with_subsampling(const TransformUserData *user_data, int scanline) { - double2 uv = user_data->start_uv + - user_data->destination_region.x_range.first() * user_data->add_x + - user_data->add_y * scanline; + /* Note: sample at pixel center for proper filtering. */ + double pixel_x = user_data->destination_region.x_range.first() + 0.5; + double pixel_y = scanline + 0.5; + double2 uv = user_data->start_uv + user_data->add_x * pixel_x + user_data->add_y * pixel_y; output.init_pixel_pointer(user_data->dst, int2(user_data->destination_region.x_range.first(), scanline)); -- 2.30.2