BLI: Add nearest interpolation with clamped boundary
This patch adds clamped boundaries variants of the nearest interpolation functions in the BLI module. The naming convention used by the bilinear functions were followed. Needed by #119414. Pull Request: https://projects.blender.org/blender/blender/pulls/119732
This commit is contained in:
parent
e2bdaf8ec7
commit
2906ea9785
|
@ -491,7 +491,7 @@ static float4 studiolight_calculate_radiance(ImBuf *ibuf, const float direction[
|
|||
{
|
||||
float uv[2];
|
||||
direction_to_equirect(uv, direction);
|
||||
return blender::imbuf::interpolate_nearest_fl(ibuf, uv[0] * ibuf->x, uv[1] * ibuf->y);
|
||||
return blender::imbuf::interpolate_nearest_border_fl(ibuf, uv[0] * ibuf->x, uv[1] * ibuf->y);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -732,10 +732,10 @@ static void studiolight_matcap_preview(uint *icon_buffer, StudioLight *sl, bool
|
|||
|
||||
float u = dx * diffuse_buffer->x - 1.0f;
|
||||
float v = dy * diffuse_buffer->y - 1.0f;
|
||||
float4 color = imbuf::interpolate_nearest_fl(diffuse_buffer, u, v);
|
||||
float4 color = imbuf::interpolate_nearest_border_fl(diffuse_buffer, u, v);
|
||||
|
||||
if (specular_buffer) {
|
||||
float4 specular = imbuf::interpolate_nearest_fl(specular_buffer, u, v);
|
||||
float4 specular = imbuf::interpolate_nearest_border_fl(specular_buffer, u, v);
|
||||
add_v3_v3(color, specular);
|
||||
}
|
||||
|
||||
|
|
|
@ -1329,7 +1329,7 @@ static void tracking_stabilize_frame_interpolation_cb(void *__restrict userdata,
|
|||
for (int x = 0; x < tmpibuf->x; x++, dst++) {
|
||||
vec[0] = float(x);
|
||||
mul_v3_m4v3(rvec, mat, vec);
|
||||
*dst = imbuf::interpolate_nearest_fl(ibuf, rvec[0], rvec[1]);
|
||||
*dst = imbuf::interpolate_nearest_border_fl(ibuf, rvec[0], rvec[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1355,7 +1355,7 @@ static void tracking_stabilize_frame_interpolation_cb(void *__restrict userdata,
|
|||
for (int x = 0; x < tmpibuf->x; x++, dst++) {
|
||||
vec[0] = float(x);
|
||||
mul_v3_m4v3(rvec, mat, vec);
|
||||
*dst = imbuf::interpolate_nearest_byte(ibuf, rvec[0], rvec[1]);
|
||||
*dst = imbuf::interpolate_nearest_border_byte(ibuf, rvec[0], rvec[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,23 +15,25 @@
|
|||
* Any filtering done on texel values just blends them without color space or
|
||||
* gamma conversions.
|
||||
*
|
||||
* Sampling completely outside the image returns transparent black.
|
||||
*/
|
||||
|
||||
#include "BLI_math_base.h"
|
||||
#include "BLI_math_base.hh"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
|
||||
namespace blender::math {
|
||||
|
||||
/**
|
||||
* Nearest (point) sampling.
|
||||
* Nearest (point) sampling (with black border).
|
||||
*
|
||||
* Returns texel at floor(u,v) integer index -- note that it is not "nearest
|
||||
* to u,v coordinate", but rather with fractional part truncated (it would be
|
||||
* "nearest" if subtracting 0.5 from input u,v).
|
||||
* Returns texel at floor(u,v) integer index. Samples outside the image are turned into transparent
|
||||
* black.
|
||||
*
|
||||
* Note that it is not "nearest to u,v coordinate", but rather with fractional part truncated (it
|
||||
* would be "nearest" if subtracting 0.5 from input u,v).
|
||||
*/
|
||||
|
||||
inline void interpolate_nearest_byte(
|
||||
inline void interpolate_nearest_border_byte(
|
||||
const uchar *buffer, uchar *output, int width, int height, float u, float v)
|
||||
{
|
||||
BLI_assert(buffer);
|
||||
|
@ -51,6 +53,67 @@ inline void interpolate_nearest_byte(
|
|||
output[3] = data[3];
|
||||
}
|
||||
|
||||
[[nodiscard]] inline uchar4 interpolate_nearest_border_byte(
|
||||
const uchar *buffer, int width, int height, float u, float v)
|
||||
{
|
||||
uchar4 res;
|
||||
interpolate_nearest_border_byte(buffer, res, width, height, u, v);
|
||||
return res;
|
||||
}
|
||||
|
||||
inline void interpolate_nearest_border_fl(
|
||||
const float *buffer, float *output, int width, int height, int components, float u, float v)
|
||||
{
|
||||
BLI_assert(buffer);
|
||||
int x = int(u);
|
||||
int y = int(v);
|
||||
|
||||
/* Outside image? */
|
||||
if (x < 0 || x >= width || y < 0 || y >= height) {
|
||||
for (int i = 0; i < components; i++) {
|
||||
output[i] = 0.0f;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const float *data = buffer + (int64_t(width) * y + x) * components;
|
||||
for (int i = 0; i < components; i++) {
|
||||
output[i] = data[i];
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] inline float4 interpolate_nearest_border_fl(
|
||||
const float *buffer, int width, int height, float u, float v)
|
||||
{
|
||||
float4 res;
|
||||
interpolate_nearest_border_fl(buffer, res, width, height, 4, u, v);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Nearest (point) sampling.
|
||||
*
|
||||
* Returns texel at floor(u,v) integer index. Samples outside the image are clamped to texels at
|
||||
* image edge.
|
||||
*
|
||||
* Note that it is not "nearest to u,v coordinate", but rather with fractional part truncated (it
|
||||
* would be "nearest" if subtracting 0.5 from input u,v).
|
||||
*/
|
||||
|
||||
inline void interpolate_nearest_byte(
|
||||
const uchar *buffer, uchar *output, int width, int height, float u, float v)
|
||||
{
|
||||
BLI_assert(buffer);
|
||||
const int x = math::clamp(int(u), 0, width - 1);
|
||||
const int y = math::clamp(int(v), 0, height - 1);
|
||||
|
||||
const uchar *data = buffer + (int64_t(width) * y + x) * 4;
|
||||
output[0] = data[0];
|
||||
output[1] = data[1];
|
||||
output[2] = data[2];
|
||||
output[3] = data[3];
|
||||
}
|
||||
|
||||
[[nodiscard]] inline uchar4 interpolate_nearest_byte(
|
||||
const uchar *buffer, int width, int height, float u, float v)
|
||||
{
|
||||
|
@ -63,16 +126,8 @@ inline void interpolate_nearest_fl(
|
|||
const float *buffer, float *output, int width, int height, int components, float u, float v)
|
||||
{
|
||||
BLI_assert(buffer);
|
||||
int x = int(u);
|
||||
int y = int(v);
|
||||
|
||||
/* Outside image? */
|
||||
if (x < 0 || x >= width || y < 0 || y >= height) {
|
||||
for (int i = 0; i < components; i++) {
|
||||
output[i] = 0.0f;
|
||||
}
|
||||
return;
|
||||
}
|
||||
const int x = math::clamp(int(u), 0, width - 1);
|
||||
const int y = math::clamp(int(v), 0, height - 1);
|
||||
|
||||
const float *data = buffer + (int64_t(width) * y + x) * components;
|
||||
for (int i = 0; i < components; i++) {
|
||||
|
|
|
@ -24,6 +24,50 @@ static constexpr float image_fl[image_height][image_width][4] = {
|
|||
{{1, 2, 3, 4}, {73, 108, 153, 251}, {128, 129, 130, 131}},
|
||||
};
|
||||
|
||||
TEST(math_interp, NearestCharExactSamples)
|
||||
{
|
||||
uchar4 res;
|
||||
uchar4 exp1 = {73, 108, 153, 251};
|
||||
res = interpolate_nearest_border_byte(image_char[0][0], image_width, image_height, 1.0f, 2.0f);
|
||||
EXPECT_EQ(exp1, res);
|
||||
uchar4 exp2 = {240, 160, 90, 20};
|
||||
res = interpolate_nearest_border_byte(image_char[0][0], image_width, image_height, 2.0f, 0.0f);
|
||||
EXPECT_EQ(exp2, res);
|
||||
}
|
||||
|
||||
TEST(math_interp, NearestCharHalfwaySamples)
|
||||
{
|
||||
uchar4 res;
|
||||
uchar4 exp1 = {0, 1, 2, 3};
|
||||
res = interpolate_nearest_border_byte(image_char[0][0], image_width, image_height, 0.5f, 1.5f);
|
||||
EXPECT_EQ(exp1, res);
|
||||
uchar4 exp2 = {255, 254, 217, 216};
|
||||
res = interpolate_nearest_border_byte(image_char[0][0], image_width, image_height, 0.5f, 0.5f);
|
||||
EXPECT_EQ(exp2, res);
|
||||
}
|
||||
|
||||
TEST(math_interp, NearestFloatExactSamples)
|
||||
{
|
||||
float4 res;
|
||||
float4 exp1 = {73.0f, 108.0f, 153.0f, 251.0f};
|
||||
res = interpolate_nearest_border_fl(image_fl[0][0], image_width, image_height, 1.0f, 2.0f);
|
||||
EXPECT_EQ(exp1, res);
|
||||
float4 exp2 = {240.0f, 160.0f, 90.0f, 20.0f};
|
||||
res = interpolate_nearest_border_fl(image_fl[0][0], image_width, image_height, 2.0f, 0.0f);
|
||||
EXPECT_EQ(exp2, res);
|
||||
}
|
||||
|
||||
TEST(math_interp, NearestFloatHalfwaySamples)
|
||||
{
|
||||
float4 res;
|
||||
float4 exp1 = {0.0f, 1.0f, 2.0f, 3.0f};
|
||||
res = interpolate_nearest_border_fl(image_fl[0][0], image_width, image_height, 0.5f, 1.5f);
|
||||
EXPECT_EQ(exp1, res);
|
||||
float4 exp2 = {255.0f, 254.0f, 217.0f, 216.0f};
|
||||
res = interpolate_nearest_border_fl(image_fl[0][0], image_width, image_height, 0.5f, 0.5f);
|
||||
EXPECT_EQ(exp2, res);
|
||||
}
|
||||
|
||||
TEST(math_interp, BilinearCharExactSamples)
|
||||
{
|
||||
uchar4 res;
|
||||
|
|
|
@ -74,7 +74,8 @@ void RenderLayersProg::do_interpolation(float output[4], float x, float y, Pixel
|
|||
|
||||
switch (sampler) {
|
||||
case PixelSampler::Nearest:
|
||||
math::interpolate_nearest_fl(input_buffer_, output, width, height, elementsize_, x, y);
|
||||
math::interpolate_nearest_border_fl(
|
||||
input_buffer_, output, width, height, elementsize_, x, y);
|
||||
break;
|
||||
case PixelSampler::Bilinear:
|
||||
math::interpolate_bilinear_border_fl(
|
||||
|
|
|
@ -504,10 +504,10 @@ template<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractD
|
|||
float xf = x / (float)texture_width;
|
||||
float u = info.clipping_uv_bounds.xmax * xf +
|
||||
info.clipping_uv_bounds.xmin * (1.0 - xf) - tile_offset_x;
|
||||
imbuf::interpolate_nearest_fl(tile_buffer,
|
||||
&extracted_buffer.float_buffer.data[offset * 4],
|
||||
u * tile_buffer->x,
|
||||
v * tile_buffer->y);
|
||||
imbuf::interpolate_nearest_border_fl(tile_buffer,
|
||||
&extracted_buffer.float_buffer.data[offset * 4],
|
||||
u * tile_buffer->x,
|
||||
v * tile_buffer->y);
|
||||
offset++;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,21 +20,21 @@ namespace blender::imbuf {
|
|||
|
||||
/* Nearest sampling. */
|
||||
|
||||
[[nodiscard]] inline uchar4 interpolate_nearest_byte(const ImBuf *in, float u, float v)
|
||||
[[nodiscard]] inline uchar4 interpolate_nearest_border_byte(const ImBuf *in, float u, float v)
|
||||
{
|
||||
return math::interpolate_nearest_byte(in->byte_buffer.data, in->x, in->y, u, v);
|
||||
return math::interpolate_nearest_border_byte(in->byte_buffer.data, in->x, in->y, u, v);
|
||||
}
|
||||
[[nodiscard]] inline float4 interpolate_nearest_fl(const ImBuf *in, float u, float v)
|
||||
[[nodiscard]] inline float4 interpolate_nearest_border_fl(const ImBuf *in, float u, float v)
|
||||
{
|
||||
return math::interpolate_nearest_fl(in->float_buffer.data, in->x, in->y, u, v);
|
||||
return math::interpolate_nearest_border_fl(in->float_buffer.data, in->x, in->y, u, v);
|
||||
}
|
||||
inline void interpolate_nearest_byte(const ImBuf *in, uchar output[4], float u, float v)
|
||||
inline void interpolate_nearest_border_byte(const ImBuf *in, uchar output[4], float u, float v)
|
||||
{
|
||||
math::interpolate_nearest_byte(in->byte_buffer.data, output, in->x, in->y, u, v);
|
||||
math::interpolate_nearest_border_byte(in->byte_buffer.data, output, in->x, in->y, u, v);
|
||||
}
|
||||
inline void interpolate_nearest_fl(const ImBuf *in, float output[4], float u, float v)
|
||||
inline void interpolate_nearest_border_fl(const ImBuf *in, float output[4], float u, float v)
|
||||
{
|
||||
math::interpolate_nearest_fl(in->float_buffer.data, output, in->x, in->y, 4, u, v);
|
||||
math::interpolate_nearest_border_fl(in->float_buffer.data, output, in->x, in->y, 4, u, v);
|
||||
}
|
||||
|
||||
/* Nearest sampling with UV wrapping. */
|
||||
|
|
|
@ -14,10 +14,10 @@ void IMB_sampleImageAtLocation(ImBuf *ibuf, float x, float y, bool make_linear_r
|
|||
{
|
||||
using namespace blender;
|
||||
if (ibuf->float_buffer.data) {
|
||||
imbuf::interpolate_nearest_fl(ibuf, color, x, y);
|
||||
imbuf::interpolate_nearest_border_fl(ibuf, color, x, y);
|
||||
}
|
||||
else {
|
||||
uchar4 byte_color = imbuf::interpolate_nearest_byte(ibuf, x, y);
|
||||
uchar4 byte_color = imbuf::interpolate_nearest_border_byte(ibuf, x, y);
|
||||
rgba_uchar_to_float(color, byte_color);
|
||||
if (make_linear_rgb) {
|
||||
IMB_colormanagement_colorspace_to_scene_linear_v4(
|
||||
|
|
|
@ -140,7 +140,7 @@ static void sample_image(const ImBuf *source, float u, float v, T *r_sample)
|
|||
}
|
||||
else if constexpr (Filter == IMB_FILTER_NEAREST && std::is_same_v<T, uchar> && NumChannels == 4)
|
||||
{
|
||||
interpolate_nearest_byte(source, r_sample, u, v);
|
||||
interpolate_nearest_border_byte(source, r_sample, u, v);
|
||||
}
|
||||
else if constexpr (Filter == IMB_FILTER_BILINEAR && std::is_same_v<T, uchar> && NumChannels == 4)
|
||||
{
|
||||
|
@ -164,7 +164,7 @@ static void sample_image(const ImBuf *source, float u, float v, T *r_sample)
|
|||
}
|
||||
}
|
||||
else if constexpr (Filter == IMB_FILTER_NEAREST && std::is_same_v<T, float>) {
|
||||
math::interpolate_nearest_fl(
|
||||
math::interpolate_nearest_border_fl(
|
||||
source->float_buffer.data, r_sample, source->x, source->y, NumChannels, u, v);
|
||||
}
|
||||
else if constexpr (Filter == IMB_FILTER_CUBIC_BSPLINE && std::is_same_v<T, float>) {
|
||||
|
|
|
@ -1573,10 +1573,10 @@ static void transform_image(int x,
|
|||
switch (interpolation) {
|
||||
case 0:
|
||||
if (dst_fl) {
|
||||
dst_fl[offset] = imbuf::interpolate_nearest_fl(ibuf, xt, yt);
|
||||
dst_fl[offset] = imbuf::interpolate_nearest_border_fl(ibuf, xt, yt);
|
||||
}
|
||||
else {
|
||||
dst_ch[offset] = imbuf::interpolate_nearest_byte(ibuf, xt, yt);
|
||||
dst_ch[offset] = imbuf::interpolate_nearest_border_byte(ibuf, xt, yt);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
|
|
Loading…
Reference in New Issue