From: Steinar H. Gunderson Date: Wed, 22 Jan 2014 23:43:58 +0000 (+0100) Subject: Add a unit test for VignetteEffect, and fix two bugs. X-Git-Tag: 1.0~62 X-Git-Url: https://git.sesse.net/?p=movit;a=commitdiff_plain;h=0f37f626e3c445593e2008357622441977fa3a32 Add a unit test for VignetteEffect, and fix two bugs. As usual, adding unit tests uncovers bugs (although one was known in this case): - Make VignetteEffect work with any aspect, not only 16:9 (known). - Make sure (0,0) is upper-left for center, not lower-left as in OpenGL. --- diff --git a/Makefile.in b/Makefile.in index fbacc46..5300cd4 100644 --- a/Makefile.in +++ b/Makefile.in @@ -53,11 +53,11 @@ TESTED_EFFECTS += resample_effect TESTED_EFFECTS += dither_effect TESTED_EFFECTS += deconvolution_sharpen_effect TESTED_EFFECTS += fft_pass_effect +TESTED_EFFECTS += vignette_effect UNTESTED_EFFECTS = sandbox_effect UNTESTED_EFFECTS += mirror_effect UNTESTED_EFFECTS += resize_effect -UNTESTED_EFFECTS += vignette_effect UNTESTED_EFFECTS += multiply_effect EFFECTS = $(TESTED_EFFECTS) $(UNTESTED_EFFECTS) diff --git a/vignette_effect.cpp b/vignette_effect.cpp index ac51977..0f5c688 100644 --- a/vignette_effect.cpp +++ b/vignette_effect.cpp @@ -7,6 +7,7 @@ VignetteEffect::VignetteEffect() : center(0.5f, 0.5f), + aspect_correction(1.0f, 1.0f), radius(0.3f), inner_radius(0.3f) { @@ -20,12 +21,22 @@ std::string VignetteEffect::output_fragment_shader() return read_file("vignette_effect.frag"); } +void VignetteEffect::inform_input_size(unsigned input_num, unsigned width, unsigned height) { + assert(input_num == 0); + if (width >= height) { + aspect_correction = Point2D(float(width) / float(height), 1.0f); + } else { + aspect_correction = Point2D(1.0f, float(height) / float(width)); + } +} + void VignetteEffect::set_gl_state(GLuint glsl_program_num, const std::string &prefix, unsigned *sampler_num) { Effect::set_gl_state(glsl_program_num, prefix, sampler_num); set_uniform_float(glsl_program_num, prefix, "pihalf_div_radius", 0.5 * M_PI / radius); + set_uniform_vec2(glsl_program_num, prefix, "aspect_correction", (float *)&aspect_correction); - Point2D aspect(16.0f / 9.0f, 1.0f); // FIXME - set_uniform_vec2(glsl_program_num, prefix, "aspect_correction", (float *)&aspect); + Point2D flipped_center(center.x, 1.0f - center.y); + set_uniform_vec2(glsl_program_num, prefix, "flipped_center", (float *)&flipped_center); } diff --git a/vignette_effect.frag b/vignette_effect.frag index 54fafff..60d4b33 100644 --- a/vignette_effect.frag +++ b/vignette_effect.frag @@ -2,13 +2,14 @@ uniform float PREFIX(pihalf_div_radius); uniform vec2 PREFIX(aspect_correction); +uniform vec2 PREFIX(flipped_center); vec4 FUNCNAME(vec2 tc) { vec4 x = INPUT(tc); const float pihalf = 0.5 * 3.14159265358979324; - vec2 normalized_pos = (tc - PREFIX(center)) * PREFIX(aspect_correction); + vec2 normalized_pos = (tc - PREFIX(flipped_center)) * PREFIX(aspect_correction); float dist = (length(normalized_pos) - PREFIX(inner_radius)) * PREFIX(pihalf_div_radius); float linear_falloff = clamp(dist, 0.0, pihalf); float falloff = cos(linear_falloff) * cos(linear_falloff); diff --git a/vignette_effect.h b/vignette_effect.h index febdc44..4501ebe 100644 --- a/vignette_effect.h +++ b/vignette_effect.h @@ -18,10 +18,11 @@ public: virtual bool needs_srgb_primaries() const { return false; } virtual AlphaHandling alpha_handling() const { return DONT_CARE_ALPHA_TYPE; } + virtual void inform_input_size(unsigned input_num, unsigned width, unsigned height); void set_gl_state(GLuint glsl_program_num, const std::string &prefix, unsigned *sampler_num); private: - Point2D center; + Point2D center, aspect_correction; float radius, inner_radius; }; diff --git a/vignette_effect_test.cpp b/vignette_effect_test.cpp new file mode 100644 index 0000000..ecd52b5 --- /dev/null +++ b/vignette_effect_test.cpp @@ -0,0 +1,92 @@ +// Unit tests for VignetteEffect. + +#include + +#include "effect_chain.h" +#include "vignette_effect.h" +#include "gtest/gtest.h" +#include "image_format.h" +#include "test_util.h" + +TEST(VignetteEffectTest, HugeInnerRadiusDoesNothing) { + const int size = 4; + + float data[size * size] = { + 0.0, 1.0, 0.0, 1.0, + 0.0, 1.0, 1.0, 0.0, + 0.0, 0.5, 1.0, 0.5, + 0.0, 0.0, 0.0, 0.0, + }; + float out_data[size * size]; + + EffectChainTester tester(data, size, size, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR); + Effect *vignette_effect = tester.get_chain()->add_effect(new VignetteEffect()); + ASSERT_TRUE(vignette_effect->set_float("inner_radius", 10.0f)); + tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR); + + expect_equal(data, out_data, size, size); +} + +TEST(VignetteEffectTest, HardCircle) { + const int size = 16; + + float data[size * size], out_data[size * size], expected_data[size * size]; + for (int y = 0; y < size; ++y) { + for (int x = 0; x < size; ++x) { + data[y * size + x] = 1.0f; + } + } + for (int y = 0; y < size; ++y) { + const float yf = (y + 0.5f) / size; + for (int x = 0; x < size; ++x) { + const float xf = (x + 0.5f) / size; + if (hypot(xf - 0.5, yf - 0.5) < 0.3) { + expected_data[y * size + x] = 1.0f; + } else { + expected_data[y * size + x] = 0.0f; + } + } + } + + EffectChainTester tester(data, size, size, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR); + Effect *vignette_effect = tester.get_chain()->add_effect(new VignetteEffect()); + ASSERT_TRUE(vignette_effect->set_float("radius", 0.0f)); + ASSERT_TRUE(vignette_effect->set_float("inner_radius", 0.3f)); + tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR); + + expect_equal(expected_data, out_data, size, size); +} + +TEST(VignetteEffectTest, BurstFromUpperLeftCorner) { + const int width = 16, height = 24; + float radius = 0.5f; + + float data[width * height], out_data[width * height], expected_data[width * height]; + for (int y = 0; y < height; ++y) { + for (int x = 0; x < width; ++x) { + data[y * width + x] = 1.0f; + } + } + for (int y = 0; y < height; ++y) { + const float yf = (y + 0.5f) / width; // Note: Division by width. + for (int x = 0; x < width; ++x) { + const float xf = (x + 0.5f) / width; + const float d = hypot(xf, yf) / radius; + if (d >= 1.0f) { + expected_data[y * width + x] = 0.0f; + } else { + expected_data[y * width + x] = cos(d * 0.5 * M_PI) * cos(d * 0.5 * M_PI); + } + } + } + + EffectChainTester tester(data, width, height, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR); + Effect *vignette_effect = tester.get_chain()->add_effect(new VignetteEffect()); + float center[] = { 0.0f, 0.0f }; + ASSERT_TRUE(vignette_effect->set_vec2("center", center)); + ASSERT_TRUE(vignette_effect->set_float("radius", radius)); + ASSERT_TRUE(vignette_effect->set_float("inner_radius", 0.0f)); + tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR); + + expect_equal(expected_data, out_data, width, height); +}