From ef1d22aea57ff2bd9f8d3dab922d7d9727d9a03b Mon Sep 17 00:00:00 2001 From: Bill Spitzak Date: Thu, 1 Aug 2024 14:01:42 +0300 Subject: [PATCH] Fix #124842: Translation leaves empty pixels at edges The Translate node leaves empty pixels at the boundary of the image. This caused by incorrect clipping when sampling the pixels. To fix this, we adjust COM_MemoryBuffer::read to read using Extend or Repeat using BLI interpolation, then multiply that by a clipping rectangle. The read_elem_sampled function is now defined in terms of the read method. This also coincidentally fixes off by half a pixel error in nearest neighbour interpolation. --- .../compositor/intern/COM_MemoryBuffer.h | 75 +++++++++++++------ tests/data | 2 +- 2 files changed, 54 insertions(+), 23 deletions(-) diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.h b/source/blender/compositor/intern/COM_MemoryBuffer.h index f957df176c1..1bc42bb1e00 100644 --- a/source/blender/compositor/intern/COM_MemoryBuffer.h +++ b/source/blender/compositor/intern/COM_MemoryBuffer.h @@ -316,18 +316,7 @@ class MemoryBuffer { void read_elem_sampled(float x, float y, PixelSampler sampler, float *out) const { - switch (sampler) { - case PixelSampler::Nearest: - read_elem_checked(x, y, out); - break; - case PixelSampler::Bilinear: - read_elem_bilinear(x, y, out); - break; - case PixelSampler::Bicubic: - /* Using same method as GPU compositor. Final results may still vary. */ - read_elem_bicubic_bspline(x, y, out); - break; - } + read(out, x, y, sampler); } void read_elem_filtered( @@ -534,17 +523,59 @@ class MemoryBuffer { MemoryBufferExtend extend_x = MemoryBufferExtend::Clip, MemoryBufferExtend extend_y = MemoryBufferExtend::Clip) const { - bool clip_x = (extend_x == MemoryBufferExtend::Clip && (x < rect_.xmin || x >= rect_.xmax)); - bool clip_y = (extend_y == MemoryBufferExtend::Clip && (y < rect_.ymin || y >= rect_.ymax)); - if (clip_x || clip_y) { - /* clip result outside rect is zero */ - memset(result, 0, num_channels_ * sizeof(float)); + // Extend is completely ignored for constants. This may need to be fixed in the future. + if (is_a_single_elem_) { + memcpy(result, buffer_, get_elem_bytes_len()); + return; } - else { - float u = x; - float v = y; - this->wrap_pixel(u, v, extend_x, extend_y); - this->read_elem_sampled(u, v, sampler, result); + + this->wrap_pixel(x, y, extend_x, extend_y); + + if (sampler == PixelSampler::Nearest) { + read_elem_checked(int(floorf(x + 0.5f)), int(floorf(y + 0.5f)), result); + return; + } + + x = get_relative_x(x); + y = get_relative_y(y); + const float w = get_width(); + const float h = get_height(); + + // compute (linear interpolation) intersection with Clip + float mult = 1.0f; + if (extend_x == MemoryBufferExtend::Clip) { + mult = std::min(x + 1.0f, w - x); + } + if (extend_y == MemoryBufferExtend::Clip) { + mult = std::min(mult, std::min(y + 1.0f, h - y)); + } + if (mult <= 0.0f) { + clear_elem(result); + return; + } + + if (sampler == PixelSampler::Bilinear) { + // Sample using Extend or Repeat + math::interpolate_bilinear_wrap_fl(buffer_, + result, + w, + h, + num_channels_, + x, + y, + extend_x == MemoryBufferExtend::Repeat, + extend_y == MemoryBufferExtend::Repeat); + } + else { // PixelSampler::Bicubic + // Sample using Extend (Repeat is not implemented by interpolate_cubic_bspline) + math::interpolate_cubic_bspline_fl(buffer_, result, w, h, num_channels_, x, y); + } + + // Multiply by Clip intersection + if (mult < 1.0f) { + for (int i = 0; i < num_channels_; ++i) { + result[i] *= mult; + } } } void write_pixel(int x, int y, const float color[4]); diff --git a/tests/data b/tests/data index 00d24c72fca..dcce0a7f179 160000 --- a/tests/data +++ b/tests/data @@ -1 +1 @@ -Subproject commit 00d24c72fcae30fc67927948c16cd87f0ac356ea +Subproject commit dcce0a7f17901871044a13157c7eb660b1272501