From a95e56b741709f7157a44196091ccad3ec369e5e Mon Sep 17 00:00:00 2001 From: Manuel Castilla Date: Mon, 23 Aug 2021 15:29:34 +0200 Subject: Compositor: Add sampling methods for full frame Current sampling methods have off by one issues on full frame: - Bilinear sampling do not fully sample bottom and left image border, creating edges. - Single elem buffers are not sampled at all when they should be at least on the borders to smooth edges. - EWA filtering is partially implemented on `ReadBufferOperation`, it needs to be moved to `MemoryBuffer` on full frame. In order to not affect tiled implementation, this commit creates specific sampling methods for full frame needs. Reviewed By: jbakker Differential Revision: https://developer.blender.org/D12164 --- .../blender/compositor/intern/COM_MemoryBuffer.h | 97 +++++++++++++++++++++- 1 file changed, 94 insertions(+), 3 deletions(-) (limited to 'source/blender/compositor/intern/COM_MemoryBuffer.h') diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.h b/source/blender/compositor/intern/COM_MemoryBuffer.h index 310e87b6a4b..d3c7566d246 100644 --- a/source/blender/compositor/intern/COM_MemoryBuffer.h +++ b/source/blender/compositor/intern/COM_MemoryBuffer.h @@ -191,23 +191,96 @@ class MemoryBuffer { void read_elem(int x, int y, float *out) const { - memcpy(out, get_elem(x, y), m_num_channels * sizeof(float)); + memcpy(out, get_elem(x, y), get_elem_bytes_len()); + } + + void read_elem_checked(int x, int y, float *out) const + { + if (x < m_rect.xmin || x >= m_rect.xmax || y < m_rect.ymin || y >= m_rect.ymax) { + clear_elem(out); + } + else { + read_elem(x, y, out); + } + } + + void read_elem_checked(float x, float y, float *out) const + { + if (x < m_rect.xmin || x >= m_rect.xmax || y < m_rect.ymin || y >= m_rect.ymax) { + clear_elem(out); + } + else { + read_elem(x, y, out); + } + } + + void read_elem_bilinear(float x, float y, float *out) const + { + /* Only clear past +/-1 borders to be able to smooth edges. */ + if (x <= m_rect.xmin - 1.0f || x >= m_rect.xmax || y <= m_rect.ymin - 1.0f || + y >= m_rect.ymax) { + clear_elem(out); + return; + } + + if (m_is_a_single_elem) { + if (x >= m_rect.xmin && x < m_rect.xmax - 1.0f && y >= m_rect.ymin && + y < m_rect.ymax - 1.0f) { + memcpy(out, m_buffer, get_elem_bytes_len()); + return; + } + + /* Do sampling at borders to smooth edges. */ + const float last_x = getWidth() - 1.0f; + const float rel_x = get_relative_x(x); + float single_x = 0.0f; + if (rel_x < 0.0f) { + single_x = rel_x; + } + else if (rel_x > last_x) { + single_x = rel_x - last_x; + } + + const float last_y = getHeight() - 1.0f; + const float rel_y = get_relative_y(y); + float single_y = 0.0f; + if (rel_y < 0.0f) { + single_y = rel_y; + } + else if (rel_y > last_y) { + single_y = rel_y - last_y; + } + + BLI_bilinear_interpolation_fl(m_buffer, out, 1, 1, m_num_channels, single_x, single_y); + return; + } + + BLI_bilinear_interpolation_fl(m_buffer, + out, + getWidth(), + getHeight(), + m_num_channels, + get_relative_x(x), + get_relative_y(y)); } void read_elem_sampled(float x, float y, PixelSampler sampler, float *out) const { switch (sampler) { case PixelSampler::Nearest: - this->read_elem(x, y, out); + read_elem_checked(x, y, out); break; case PixelSampler::Bilinear: case PixelSampler::Bicubic: /* No bicubic. Current implementation produces fuzzy results. */ - this->readBilinear(out, x, y); + read_elem_bilinear(x, y, out); break; } } + void read_elem_filtered( + const float x, const float y, float dx[2], float dy[2], float *out) const; + /** * Get channel value at given coordinates. */ @@ -403,6 +476,8 @@ class MemoryBuffer { y = y + m_rect.ymin; } + /* TODO(manzanilla): to be removed with tiled implementation. For applying #MemoryBufferExtend + * use #wrap_pixel. */ inline void read(float *result, int x, int y, @@ -425,6 +500,7 @@ class MemoryBuffer { } } + /* TODO(manzanilla): to be removed with tiled implementation. */ inline void readNoCheck(float *result, int x, int y, @@ -582,6 +658,21 @@ class MemoryBuffer { return get_memory_width() * get_memory_height(); } + void clear_elem(float *out) const + { + memset(out, 0, this->m_num_channels * sizeof(float)); + } + + template T get_relative_x(T x) const + { + return x - m_rect.xmin; + } + + template T get_relative_y(T y) const + { + return y - m_rect.ymin; + } + void copy_single_elem_from(const MemoryBuffer *src, int channel_offset, int elem_size, -- cgit v1.2.3