VSE: add Cubic Mitchell filtering, rename previous cubic to Cubic BSpline #117517
|
@ -203,6 +203,27 @@ void interpolate_bilinear_wrap_fl(const float *buffer,
|
||||||
void interpolate_cubic_bspline_fl(
|
void interpolate_cubic_bspline_fl(
|
||||||
const float *buffer, float *output, int width, int height, int components, float u, float v);
|
const float *buffer, float *output, int width, int height, int components, float u, float v);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cubic Mitchell sampling.
|
||||||
|
*
|
||||||
|
* Takes 4x4 image samples at floor(u,v)-1 .. floor(u,v)+2, and blends them
|
||||||
|
* based on fractional parts of u,v. Uses Mitchell-Netravali filter (B=C=1/3),
|
||||||
|
* which has a good compromise between blur and ringing.
|
||||||
|
* Samples outside the image are clamped to texels at image edge.
|
||||||
|
*
|
||||||
|
* Note that you probably want to subtract 0.5 from u,v before this function,
|
||||||
|
* to get proper filtering.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[[nodiscard]] uchar4 interpolate_cubic_mitchell_byte(
|
||||||
|
const uchar *buffer, int width, int height, float u, float v);
|
||||||
|
|
||||||
|
[[nodiscard]] float4 interpolate_cubic_mitchell_fl(
|
||||||
|
const float *buffer, int width, int height, float u, float v);
|
||||||
|
|
||||||
|
void interpolate_cubic_mitchell_fl(
|
||||||
|
const float *buffer, float *output, int width, int height, int components, float u, float v);
|
||||||
|
|
||||||
} // namespace blender::math
|
} // namespace blender::math
|
||||||
|
|
||||||
#define EWA_MAXIDX 255
|
#define EWA_MAXIDX 255
|
||||||
|
|
|
@ -10,24 +10,41 @@
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
#include "BLI_math_base.h"
|
#include "BLI_math_base.h"
|
||||||
|
#include "BLI_math_base.hh"
|
||||||
#include "BLI_math_interp.hh"
|
#include "BLI_math_interp.hh"
|
||||||
#include "BLI_math_vector.h"
|
#include "BLI_math_vector.h"
|
||||||
#include "BLI_math_vector_types.hh"
|
#include "BLI_math_vector_types.hh"
|
||||||
#include "BLI_simd.h"
|
#include "BLI_simd.h"
|
||||||
#include "BLI_strict_flags.h"
|
#include "BLI_strict_flags.h"
|
||||||
|
|
||||||
/* Cubic B-Spline coefficients. f is offset from texel center in pixel space.
|
enum class eCubicFilter {
|
||||||
* This is Mitchell-Netravali filter with B=1, C=0 parameters. */
|
BSpline,
|
||||||
static blender::float4 cubic_bspline_coefficients(float f)
|
Mitchell,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Calculate cubic filter coefficients, for samples at -1,0,+1,+2.
|
||||||
|
* f is 0..1 offset from texel center in pixel space. */
|
||||||
|
template<enum eCubicFilter filter> static blender::float4 cubic_filter_coefficients(float f)
|
||||||
{
|
{
|
||||||
float f2 = f * f;
|
float f2 = f * f;
|
||||||
float f3 = f2 * f;
|
float f3 = f2 * f;
|
||||||
|
|
||||||
float w3 = f3 / 6.0f;
|
if constexpr (filter == eCubicFilter::BSpline) {
|
||||||
float w0 = -w3 + f2 * 0.5f - f * 0.5f + 1.0f / 6.0f;
|
/* Cubic B-Spline (Mitchell-Netravali filter with B=1, C=0 parameters). */
|
||||||
float w1 = f3 * 0.5f - f2 * 1.0f + 2.0f / 3.0f;
|
float w3 = f3 * (1.0f / 6.0f);
|
||||||
float w2 = 1.0f - w0 - w1 - w3;
|
float w0 = -w3 + f2 * 0.5f - f * 0.5f + 1.0f / 6.0f;
|
||||||
return blender::float4(w0, w1, w2, w3);
|
float w1 = f3 * 0.5f - f2 * 1.0f + 2.0f / 3.0f;
|
||||||
|
float w2 = 1.0f - w0 - w1 - w3;
|
||||||
|
return blender::float4(w0, w1, w2, w3);
|
||||||
|
}
|
||||||
|
else if constexpr (filter == eCubicFilter::Mitchell) {
|
||||||
|
/* Cubic Mitchell-Netravali filter with B=1/3, C=1/3 parameters. */
|
||||||
|
float w0 = -7.0f / 18.0f * f3 + 5.0f / 6.0f * f2 - 0.5f * f + 1.0f / 18.0f;
|
||||||
|
float w1 = 7.0f / 6.0f * f3 - 2.0f * f2 + 8.0f / 9.0f;
|
||||||
|
float w2 = -7.0f / 6.0f * f3 + 3.0f / 2.0f * f2 + 0.5f * f + 1.0f / 18.0f;
|
||||||
|
float w3 = 7.0f / 18.0f * f3 - 1.0f / 3.0f * f2;
|
||||||
|
return blender::float4(w0, w1, w2, w3);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if BLI_HAVE_SSE2
|
#if BLI_HAVE_SSE2
|
||||||
|
@ -51,6 +68,7 @@ BLI_INLINE __m128 floor_simd(__m128 v)
|
||||||
return v_floor;
|
return v_floor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<eCubicFilter filter>
|
||||||
BLI_INLINE void bicubic_interpolation_uchar_simd(
|
BLI_INLINE void bicubic_interpolation_uchar_simd(
|
||||||
const uchar *src_buffer, uchar *output, int width, int height, float u, float v)
|
const uchar *src_buffer, uchar *output, int width, int height, float u, float v)
|
||||||
{
|
{
|
||||||
|
@ -72,8 +90,8 @@ BLI_INLINE void bicubic_interpolation_uchar_simd(
|
||||||
__m128 frac_uv = _mm_sub_ps(uv, uv_floor);
|
__m128 frac_uv = _mm_sub_ps(uv, uv_floor);
|
||||||
|
|
||||||
/* Calculate pixel weights. */
|
/* Calculate pixel weights. */
|
||||||
blender::float4 wx = cubic_bspline_coefficients(_mm_cvtss_f32(frac_uv));
|
blender::float4 wx = cubic_filter_coefficients<filter>(_mm_cvtss_f32(frac_uv));
|
||||||
blender::float4 wy = cubic_bspline_coefficients(
|
blender::float4 wy = cubic_filter_coefficients<filter>(
|
||||||
_mm_cvtss_f32(_mm_shuffle_ps(frac_uv, frac_uv, 1)));
|
_mm_cvtss_f32(_mm_shuffle_ps(frac_uv, frac_uv, 1)));
|
||||||
|
|
||||||
/* Read 4x4 source pixels and blend them. */
|
/* Read 4x4 source pixels and blend them. */
|
||||||
|
@ -102,7 +120,8 @@ BLI_INLINE void bicubic_interpolation_uchar_simd(
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Pack and write to destination: pack to 16 bit signed, then to 8 bit
|
/* Pack and write to destination: pack to 16 bit signed, then to 8 bit
|
||||||
* unsigned, then write resulting 32-bit value. */
|
* unsigned, then write resulting 32-bit value. This will clamp
|
||||||
|
* out of range values too. */
|
||||||
out = _mm_add_ps(out, _mm_set1_ps(0.5f));
|
out = _mm_add_ps(out, _mm_set1_ps(0.5f));
|
||||||
__m128i rgba32 = _mm_cvttps_epi32(out);
|
__m128i rgba32 = _mm_cvttps_epi32(out);
|
||||||
__m128i rgba16 = _mm_packs_epi32(rgba32, _mm_setzero_si128());
|
__m128i rgba16 = _mm_packs_epi32(rgba32, _mm_setzero_si128());
|
||||||
|
@ -111,7 +130,7 @@ BLI_INLINE void bicubic_interpolation_uchar_simd(
|
||||||
}
|
}
|
||||||
#endif /* BLI_HAVE_SSE2 */
|
#endif /* BLI_HAVE_SSE2 */
|
||||||
|
|
||||||
template<typename T>
|
template<typename T, eCubicFilter filter>
|
||||||
static void bicubic_interpolation(
|
static void bicubic_interpolation(
|
||||||
const T *src_buffer, T *output, int width, int height, int components, float u, float v)
|
const T *src_buffer, T *output, int width, int height, int components, float u, float v)
|
||||||
{
|
{
|
||||||
|
@ -122,7 +141,7 @@ static void bicubic_interpolation(
|
||||||
#if BLI_HAVE_SSE2
|
#if BLI_HAVE_SSE2
|
||||||
if constexpr (std::is_same_v<T, uchar>) {
|
if constexpr (std::is_same_v<T, uchar>) {
|
||||||
if (components == 4) {
|
if (components == 4) {
|
||||||
bicubic_interpolation_uchar_simd(src_buffer, output, width, height, u, v);
|
bicubic_interpolation_uchar_simd<filter>(src_buffer, output, width, height, u, v);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -143,8 +162,8 @@ static void bicubic_interpolation(
|
||||||
float4 out{0.0f};
|
float4 out{0.0f};
|
||||||
|
|
||||||
/* Calculate pixel weights. */
|
/* Calculate pixel weights. */
|
||||||
float4 wx = cubic_bspline_coefficients(frac_u);
|
float4 wx = cubic_filter_coefficients<filter>(frac_u);
|
||||||
float4 wy = cubic_bspline_coefficients(frac_v);
|
float4 wy = cubic_filter_coefficients<filter>(frac_v);
|
||||||
|
|
||||||
/* Read 4x4 source pixels and blend them. */
|
/* Read 4x4 source pixels and blend them. */
|
||||||
for (int n = 0; n < 4; n++) {
|
for (int n = 0; n < 4; n++) {
|
||||||
|
@ -175,6 +194,16 @@ static void bicubic_interpolation(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Mitchell filter has negative lobes; prevent output from going out of range. */
|
||||||
|
if constexpr (filter == eCubicFilter::Mitchell) {
|
||||||
|
for (int i = 0; i < components; i++) {
|
||||||
|
out[i] = math::max(out[i], 0.0f);
|
||||||
|
if constexpr (std::is_same_v<T, uchar>) {
|
||||||
|
out[i] = math::min(out[i], 255.0f);
|
||||||
aras_p marked this conversation as resolved
|
|||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Write result. */
|
/* Write result. */
|
||||||
if constexpr (std::is_same_v<T, float>) {
|
if constexpr (std::is_same_v<T, float>) {
|
||||||
if (components == 1) {
|
if (components == 1) {
|
||||||
|
@ -551,21 +580,44 @@ float4 interpolate_bilinear_wrap_fl(const float *buffer, int width, int height,
|
||||||
uchar4 interpolate_cubic_bspline_byte(const uchar *buffer, int width, int height, float u, float v)
|
uchar4 interpolate_cubic_bspline_byte(const uchar *buffer, int width, int height, float u, float v)
|
||||||
{
|
{
|
||||||
uchar4 res;
|
uchar4 res;
|
||||||
bicubic_interpolation<uchar>(buffer, res, width, height, 4, u, v);
|
bicubic_interpolation<uchar, eCubicFilter::BSpline>(buffer, res, width, height, 4, u, v);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
float4 interpolate_cubic_bspline_fl(const float *buffer, int width, int height, float u, float v)
|
float4 interpolate_cubic_bspline_fl(const float *buffer, int width, int height, float u, float v)
|
||||||
{
|
{
|
||||||
float4 res;
|
float4 res;
|
||||||
bicubic_interpolation<float>(buffer, res, width, height, 4, u, v);
|
bicubic_interpolation<float, eCubicFilter::BSpline>(buffer, res, width, height, 4, u, v);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
void interpolate_cubic_bspline_fl(
|
void interpolate_cubic_bspline_fl(
|
||||||
const float *buffer, float *output, int width, int height, int components, float u, float v)
|
const float *buffer, float *output, int width, int height, int components, float u, float v)
|
||||||
{
|
{
|
||||||
bicubic_interpolation<float>(buffer, output, width, height, components, u, v);
|
bicubic_interpolation<float, eCubicFilter::BSpline>(
|
||||||
|
buffer, output, width, height, components, u, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
uchar4 interpolate_cubic_mitchell_byte(
|
||||||
|
const uchar *buffer, int width, int height, float u, float v)
|
||||||
|
{
|
||||||
|
uchar4 res;
|
||||||
|
bicubic_interpolation<uchar, eCubicFilter::Mitchell>(buffer, res, width, height, 4, u, v);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
float4 interpolate_cubic_mitchell_fl(const float *buffer, int width, int height, float u, float v)
|
||||||
|
{
|
||||||
|
float4 res;
|
||||||
|
bicubic_interpolation<float, eCubicFilter::Mitchell>(buffer, res, width, height, 4, u, v);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void interpolate_cubic_mitchell_fl(
|
||||||
|
const float *buffer, float *output, int width, int height, int components, float u, float v)
|
||||||
|
{
|
||||||
|
bicubic_interpolation<float, eCubicFilter::Mitchell>(
|
||||||
|
buffer, output, width, height, components, u, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace blender::math
|
} // namespace blender::math
|
||||||
|
|
|
@ -256,3 +256,65 @@ TEST(math_interp, CubicBSplineCharFullyOutsideImage)
|
||||||
res = interpolate_cubic_bspline_byte(image_char[0][0], image_width, image_height, 0, 500.0f);
|
res = interpolate_cubic_bspline_byte(image_char[0][0], image_width, image_height, 0, 500.0f);
|
||||||
EXPECT_EQ(exp, res);
|
EXPECT_EQ(exp, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(math_interp, CubicMitchellCharExactSamples)
|
||||||
|
{
|
||||||
|
uchar4 res;
|
||||||
|
uchar4 exp1 = {72, 101, 140, 223};
|
||||||
|
res = interpolate_cubic_mitchell_byte(image_char[0][0], image_width, image_height, 1.0f, 2.0f);
|
||||||
|
EXPECT_EQ(int4(exp1), int4(res));
|
||||||
|
uchar4 exp2 = {233, 162, 99, 37};
|
||||||
|
res = interpolate_cubic_mitchell_byte(image_char[0][0], image_width, image_height, 2.0f, 0.0f);
|
||||||
|
EXPECT_EQ(int4(exp2), int4(res));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(math_interp, CubicMitchellCharSamples)
|
||||||
|
{
|
||||||
|
uchar4 res;
|
||||||
|
uchar4 exp1 = {135, 132, 130, 127};
|
||||||
|
res = interpolate_cubic_mitchell_byte(
|
||||||
|
image_char[0][0], image_width, image_height, 1.25f, 0.625f);
|
||||||
|
EXPECT_EQ(int4(exp1), int4(res));
|
||||||
|
uchar4 exp2 = {216, 189, 167, 143};
|
||||||
|
res = interpolate_cubic_mitchell_byte(image_char[0][0], image_width, image_height, 1.4f, 0.1f);
|
||||||
|
EXPECT_EQ(int4(exp2), int4(res));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(math_interp, CubicMitchellFloatSamples)
|
||||||
|
{
|
||||||
|
float4 res;
|
||||||
|
float4 exp1 = {134.5659f, 131.91309f, 130.17685f, 126.66989f};
|
||||||
|
res = interpolate_cubic_mitchell_fl(image_fl[0][0], image_width, image_height, 1.25f, 0.625f);
|
||||||
|
EXPECT_V4_NEAR(exp1, res, float_tolerance);
|
||||||
|
float4 exp2 = {216.27115f, 189.30673f, 166.93599f, 143.31964f};
|
||||||
|
res = interpolate_cubic_mitchell_fl(image_fl[0][0], image_width, image_height, 1.4f, 0.1f);
|
||||||
|
EXPECT_V4_NEAR(exp2, res, float_tolerance);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(math_interp, CubicMitchellCharPartiallyOutsideImage)
|
||||||
|
{
|
||||||
|
uchar4 res;
|
||||||
|
uchar4 exp1 = {0, 0, 0, 0};
|
||||||
|
res = interpolate_cubic_mitchell_byte(image_char[0][0], image_width, image_height, -0.5f, 2.0f);
|
||||||
|
EXPECT_EQ(int4(exp1), int4(res));
|
||||||
|
uchar4 exp2 = {88, 116, 151, 228};
|
||||||
|
res = interpolate_cubic_mitchell_byte(image_char[0][0], image_width, image_height, 1.25f, 2.9f);
|
||||||
|
EXPECT_EQ(int4(exp2), int4(res));
|
||||||
|
uchar4 exp3 = {239, 159, 89, 19};
|
||||||
|
res = interpolate_cubic_mitchell_byte(image_char[0][0], image_width, image_height, 2.2f, -0.1f);
|
||||||
|
EXPECT_EQ(int4(exp3), int4(res));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(math_interp, CubicMitchellFloatPartiallyOutsideImage)
|
||||||
|
{
|
||||||
|
float4 res;
|
||||||
|
float4 exp1 = {0, 0, 0, 0};
|
||||||
|
res = interpolate_cubic_mitchell_fl(image_fl[0][0], image_width, image_height, -0.5f, 2.0f);
|
||||||
|
EXPECT_V4_NEAR(exp1, res, float_tolerance);
|
||||||
|
float4 exp2 = {87.98676f, 115.63634f, 151.13014f, 228.19823f};
|
||||||
|
res = interpolate_cubic_mitchell_fl(image_fl[0][0], image_width, image_height, 1.25f, 2.9f);
|
||||||
|
EXPECT_V4_NEAR(exp2, res, float_tolerance);
|
||||||
|
float4 exp3 = {238.6136f, 158.58293f, 88.55761f, 18.53225f};
|
||||||
|
res = interpolate_cubic_mitchell_fl(image_fl[0][0], image_width, image_height, 2.2f, -0.1f);
|
||||||
|
EXPECT_V4_NEAR(exp3, res, float_tolerance);
|
||||||
|
}
|
||||||
|
|
|
@ -264,7 +264,8 @@ void IMB_rectblend_threaded(ImBuf *dbuf,
|
||||||
enum eIMBInterpolationFilterMode {
|
enum eIMBInterpolationFilterMode {
|
||||||
IMB_FILTER_NEAREST,
|
IMB_FILTER_NEAREST,
|
||||||
IMB_FILTER_BILINEAR,
|
IMB_FILTER_BILINEAR,
|
||||||
IMB_FILTER_BICUBIC,
|
IMB_FILTER_CUBIC_BSPLINE,
|
||||||
|
IMB_FILTER_CUBIC_MITCHELL,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -91,6 +91,16 @@ inline void interpolate_cubic_bspline_fl(const ImBuf *in, float output[4], float
|
||||||
memcpy(output, &col, sizeof(col));
|
memcpy(output, &col, sizeof(col));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] inline uchar4 interpolate_cubic_mitchell_byte(const ImBuf *in, float u, float v)
|
||||||
|
{
|
||||||
|
return math::interpolate_cubic_mitchell_byte(in->byte_buffer.data, in->x, in->y, u, v);
|
||||||
|
}
|
||||||
|
inline void interpolate_cubic_mitchell_byte(const ImBuf *in, uchar output[4], float u, float v)
|
||||||
|
{
|
||||||
|
uchar4 col = math::interpolate_cubic_mitchell_byte(in->byte_buffer.data, in->x, in->y, u, v);
|
||||||
|
memcpy(output, &col, sizeof(col));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace blender::imbuf
|
} // namespace blender::imbuf
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -146,10 +146,10 @@ static void sample_image(const ImBuf *source, float u, float v, T *r_sample)
|
||||||
u = wrap_uv(u, source->x);
|
u = wrap_uv(u, source->x);
|
||||||
v = wrap_uv(v, source->y);
|
v = wrap_uv(v, source->y);
|
||||||
}
|
}
|
||||||
/* BLI_bilinear_interpolation functions use `floor(uv)` and `floor(uv)+1`
|
/* Bilinear/cubic interpolation functions use `floor(uv)` and `floor(uv)+1`
|
||||||
* texels. For proper mapping between pixel and texel spaces, need to
|
* texels. For proper mapping between pixel and texel spaces, need to
|
||||||
* subtract 0.5. Same for bicubic. */
|
* subtract 0.5. */
|
||||||
if constexpr (ELEM(Filter, IMB_FILTER_BILINEAR, IMB_FILTER_BICUBIC)) {
|
if constexpr (Filter != IMB_FILTER_NEAREST) {
|
||||||
u -= 0.5f;
|
u -= 0.5f;
|
||||||
v -= 0.5f;
|
v -= 0.5f;
|
||||||
}
|
}
|
||||||
|
@ -185,14 +185,24 @@ static void sample_image(const ImBuf *source, float u, float v, T *r_sample)
|
||||||
math::interpolate_nearest_fl(
|
math::interpolate_nearest_fl(
|
||||||
source->float_buffer.data, r_sample, source->x, source->y, NumChannels, u, v);
|
source->float_buffer.data, r_sample, source->x, source->y, NumChannels, u, v);
|
||||||
}
|
}
|
||||||
else if constexpr (Filter == IMB_FILTER_BICUBIC && std::is_same_v<T, float>) {
|
else if constexpr (Filter == IMB_FILTER_CUBIC_BSPLINE && std::is_same_v<T, float>) {
|
||||||
math::interpolate_cubic_bspline_fl(
|
math::interpolate_cubic_bspline_fl(
|
||||||
source->float_buffer.data, r_sample, source->x, source->y, NumChannels, u, v);
|
source->float_buffer.data, r_sample, source->x, source->y, NumChannels, u, v);
|
||||||
}
|
}
|
||||||
else if constexpr (Filter == IMB_FILTER_BICUBIC && std::is_same_v<T, uchar> && NumChannels == 4)
|
else if constexpr (Filter == IMB_FILTER_CUBIC_BSPLINE && std::is_same_v<T, uchar> &&
|
||||||
|
NumChannels == 4)
|
||||||
{
|
{
|
||||||
interpolate_cubic_bspline_byte(source, r_sample, u, v);
|
interpolate_cubic_bspline_byte(source, r_sample, u, v);
|
||||||
}
|
}
|
||||||
|
else if constexpr (Filter == IMB_FILTER_CUBIC_MITCHELL && std::is_same_v<T, float>) {
|
||||||
|
math::interpolate_cubic_mitchell_fl(
|
||||||
|
source->float_buffer.data, r_sample, source->x, source->y, NumChannels, u, v);
|
||||||
|
}
|
||||||
|
else if constexpr (Filter == IMB_FILTER_CUBIC_MITCHELL && std::is_same_v<T, uchar> &&
|
||||||
|
NumChannels == 4)
|
||||||
|
{
|
||||||
|
interpolate_cubic_mitchell_byte(source, r_sample, u, v);
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
/* Unsupported sampler. */
|
/* Unsupported sampler. */
|
||||||
BLI_assert_unreachable();
|
BLI_assert_unreachable();
|
||||||
|
@ -385,8 +395,11 @@ void IMB_transform(const ImBuf *src,
|
||||||
else if (filter == IMB_FILTER_BILINEAR) {
|
else if (filter == IMB_FILTER_BILINEAR) {
|
||||||
transform_scanlines_filter<IMB_FILTER_BILINEAR>(ctx, y_range);
|
transform_scanlines_filter<IMB_FILTER_BILINEAR>(ctx, y_range);
|
||||||
}
|
}
|
||||||
else if (filter == IMB_FILTER_BICUBIC) {
|
else if (filter == IMB_FILTER_CUBIC_BSPLINE) {
|
||||||
Sergey Sharybin
commented
This should probably become a This should probably become a `switch` statement.
|
|||||||
transform_scanlines_filter<IMB_FILTER_BICUBIC>(ctx, y_range);
|
transform_scanlines_filter<IMB_FILTER_CUBIC_BSPLINE>(ctx, y_range);
|
||||||
|
}
|
||||||
|
else if (filter == IMB_FILTER_CUBIC_MITCHELL) {
|
||||||
|
transform_scanlines_filter<IMB_FILTER_CUBIC_MITCHELL>(ctx, y_range);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,9 +87,9 @@ TEST(imbuf_transform, bilinear_2x_smaller)
|
||||||
IMB_freeImBuf(res);
|
IMB_freeImBuf(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(imbuf_transform, bicubic_2x_smaller)
|
TEST(imbuf_transform, cubic_bspline_2x_smaller)
|
||||||
{
|
{
|
||||||
ImBuf *res = transform_2x_smaller(IMB_FILTER_BICUBIC, 1);
|
ImBuf *res = transform_2x_smaller(IMB_FILTER_CUBIC_BSPLINE, 1);
|
||||||
const ColorTheme4b *got = reinterpret_cast<ColorTheme4b *>(res->byte_buffer.data);
|
const ColorTheme4b *got = reinterpret_cast<ColorTheme4b *>(res->byte_buffer.data);
|
||||||
EXPECT_EQ(got[0], ColorTheme4b(189, 126, 62, 250));
|
EXPECT_EQ(got[0], ColorTheme4b(189, 126, 62, 250));
|
||||||
EXPECT_EQ(got[1], ColorTheme4b(134, 57, 33, 26));
|
EXPECT_EQ(got[1], ColorTheme4b(134, 57, 33, 26));
|
||||||
|
@ -97,16 +97,26 @@ TEST(imbuf_transform, bicubic_2x_smaller)
|
||||||
IMB_freeImBuf(res);
|
IMB_freeImBuf(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(imbuf_transform, bicubic_fractional_larger)
|
TEST(imbuf_transform, cubic_mitchell_2x_smaller)
|
||||||
{
|
{
|
||||||
ImBuf *res = transform_fractional_larger(IMB_FILTER_BICUBIC, 1);
|
ImBuf *res = transform_2x_smaller(IMB_FILTER_CUBIC_MITCHELL, 1);
|
||||||
const ColorTheme4b *got = reinterpret_cast<ColorTheme4b *>(res->byte_buffer.data);
|
const ColorTheme4b *got = reinterpret_cast<ColorTheme4b *>(res->byte_buffer.data);
|
||||||
EXPECT_EQ(got[0 + 0 * res->x], ColorTheme4b(35, 11, 1, 255));
|
EXPECT_EQ(got[0], ColorTheme4b(195, 130, 67, 255));
|
||||||
EXPECT_EQ(got[1 + 0 * res->x], ColorTheme4b(131, 12, 6, 250));
|
EXPECT_EQ(got[1], ColorTheme4b(132, 51, 28, 0));
|
||||||
EXPECT_EQ(got[7 + 0 * res->x], ColorTheme4b(54, 93, 19, 249));
|
EXPECT_EQ(got[2], ColorTheme4b(52, 52, 48, 255));
|
||||||
EXPECT_EQ(got[2 + 2 * res->x], ColorTheme4b(206, 70, 56, 192));
|
IMB_freeImBuf(res);
|
||||||
EXPECT_EQ(got[3 + 2 * res->x], ColorTheme4b(165, 60, 42, 78));
|
}
|
||||||
EXPECT_EQ(got[8 + 6 * res->x], ColorTheme4b(57, 1, 90, 252));
|
|
||||||
|
TEST(imbuf_transform, cubic_mitchell_fractional_larger)
|
||||||
|
{
|
||||||
|
ImBuf *res = transform_fractional_larger(IMB_FILTER_CUBIC_MITCHELL, 1);
|
||||||
|
const ColorTheme4b *got = reinterpret_cast<ColorTheme4b *>(res->byte_buffer.data);
|
||||||
|
EXPECT_EQ(got[0 + 0 * res->x], ColorTheme4b(0, 0, 0, 255));
|
||||||
|
EXPECT_EQ(got[1 + 0 * res->x], ColorTheme4b(127, 0, 0, 255));
|
||||||
|
EXPECT_EQ(got[7 + 0 * res->x], ColorTheme4b(49, 109, 13, 255));
|
||||||
|
EXPECT_EQ(got[2 + 2 * res->x], ColorTheme4b(236, 53, 50, 215));
|
||||||
|
EXPECT_EQ(got[3 + 2 * res->x], ColorTheme4b(155, 55, 35, 54));
|
||||||
|
EXPECT_EQ(got[8 + 6 * res->x], ColorTheme4b(57, 0, 98, 252));
|
||||||
IMB_freeImBuf(res);
|
IMB_freeImBuf(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -839,7 +839,8 @@ enum {
|
||||||
SEQ_TRANSFORM_FILTER_NEAREST = 0,
|
SEQ_TRANSFORM_FILTER_NEAREST = 0,
|
||||||
SEQ_TRANSFORM_FILTER_BILINEAR = 1,
|
SEQ_TRANSFORM_FILTER_BILINEAR = 1,
|
||||||
SEQ_TRANSFORM_FILTER_NEAREST_3x3 = 2,
|
SEQ_TRANSFORM_FILTER_NEAREST_3x3 = 2,
|
||||||
SEQ_TRANSFORM_FILTER_BICUBIC = 3,
|
SEQ_TRANSFORM_FILTER_CUBIC_BSPLINE = 3,
|
||||||
|
SEQ_TRANSFORM_FILTER_CUBIC_MITCHELL = 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef enum eSeqChannelFlag {
|
typedef enum eSeqChannelFlag {
|
||||||
|
|
|
@ -1709,14 +1709,28 @@ static void rna_def_strip_crop(BlenderRNA *brna)
|
||||||
}
|
}
|
||||||
|
|
||||||
static const EnumPropertyItem transform_filter_items[] = {
|
static const EnumPropertyItem transform_filter_items[] = {
|
||||||
{SEQ_TRANSFORM_FILTER_NEAREST, "NEAREST", 0, "Nearest", ""},
|
{SEQ_TRANSFORM_FILTER_NEAREST, "NEAREST", 0, "Nearest", "Use nearest sample"},
|
||||||
{SEQ_TRANSFORM_FILTER_BILINEAR, "BILINEAR", 0, "Bilinear", ""},
|
{SEQ_TRANSFORM_FILTER_BILINEAR,
|
||||||
{SEQ_TRANSFORM_FILTER_BICUBIC, "BICUBIC", 0, "Bicubic", ""},
|
"BILINEAR",
|
||||||
|
0,
|
||||||
|
"Bilinear",
|
||||||
|
"Interpolate between 2" BLI_STR_UTF8_MULTIPLICATION_SIGN "2 samples"},
|
||||||
|
{SEQ_TRANSFORM_FILTER_CUBIC_MITCHELL,
|
||||||
|
"CUBIC_MITCHELL",
|
||||||
|
0,
|
||||||
|
"Cubic Mitchell",
|
||||||
|
"Cubic Mitchell filter on 4" BLI_STR_UTF8_MULTIPLICATION_SIGN "4 samples"},
|
||||||
|
{SEQ_TRANSFORM_FILTER_CUBIC_BSPLINE,
|
||||||
|
"CUBIC_BSPLINE",
|
||||||
|
0,
|
||||||
|
"Cubic B-Spline",
|
||||||
|
"Cubic B-Spline filter (blurry but no ringing) on 4" BLI_STR_UTF8_MULTIPLICATION_SIGN
|
||||||
|
"4 samples"},
|
||||||
{SEQ_TRANSFORM_FILTER_NEAREST_3x3,
|
{SEQ_TRANSFORM_FILTER_NEAREST_3x3,
|
||||||
"SUBSAMPLING_3x3",
|
"SUBSAMPLING_3x3",
|
||||||
0,
|
0,
|
||||||
"Subsampling (3" BLI_STR_UTF8_MULTIPLICATION_SIGN "3)",
|
"Subsampling (3" BLI_STR_UTF8_MULTIPLICATION_SIGN "3)",
|
||||||
"Use nearest with 3" BLI_STR_UTF8_MULTIPLICATION_SIGN "3 subsamples during rendering"},
|
"Use nearest with 3" BLI_STR_UTF8_MULTIPLICATION_SIGN "3 subsamples"},
|
||||||
{0, nullptr, 0, nullptr, nullptr},
|
{0, nullptr, 0, nullptr, nullptr},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -545,8 +545,11 @@ static void sequencer_preprocess_transform_crop(
|
||||||
case SEQ_TRANSFORM_FILTER_BILINEAR:
|
case SEQ_TRANSFORM_FILTER_BILINEAR:
|
||||||
filter = IMB_FILTER_BILINEAR;
|
filter = IMB_FILTER_BILINEAR;
|
||||||
break;
|
break;
|
||||||
case SEQ_TRANSFORM_FILTER_BICUBIC:
|
case SEQ_TRANSFORM_FILTER_CUBIC_BSPLINE:
|
||||||
filter = IMB_FILTER_BICUBIC;
|
filter = IMB_FILTER_CUBIC_BSPLINE;
|
||||||
|
break;
|
||||||
|
case SEQ_TRANSFORM_FILTER_CUBIC_MITCHELL:
|
||||||
|
filter = IMB_FILTER_CUBIC_MITCHELL;
|
||||||
break;
|
break;
|
||||||
case SEQ_TRANSFORM_FILTER_NEAREST_3x3:
|
case SEQ_TRANSFORM_FILTER_NEAREST_3x3:
|
||||||
filter = IMB_FILTER_NEAREST;
|
filter = IMB_FILTER_NEAREST;
|
||||||
|
|
Loading…
Reference in New Issue
Not very obvious why negative lobe could lead to values above 255.