]> git.sesse.net Git - movit/commitdiff
Merge branch 'master' into epoxy
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Mon, 10 Mar 2014 00:28:46 +0000 (01:28 +0100)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Mon, 10 Mar 2014 00:31:54 +0000 (01:31 +0100)
13 files changed:
.gitignore
Makefile.in
flat_input.cpp
flat_input.h
image_format.h
slice_effect.cpp [new file with mode: 0644]
slice_effect.frag [new file with mode: 0644]
slice_effect.h [new file with mode: 0644]
slice_effect_test.cpp [new file with mode: 0644]
test_util.cpp
test_util.h
util.cpp
util.h

index 489d4a7b2f70d2a94adcb953908c1cb2a4ecd117..3da5e6cadaebf859695ec7890c34e1dd0655c16b 100644 (file)
@@ -1,4 +1,6 @@
 *.o
+*.lo
+*.la
 *.a
 *.d
 *.jpg
index 7d4a645b53112e4d1bb9953276da708373efaa4e..660bf7aba58c807be087161f3bcebd11aa7f95ac 100644 (file)
@@ -59,6 +59,7 @@ TESTED_EFFECTS += dither_effect
 TESTED_EFFECTS += deconvolution_sharpen_effect
 TESTED_EFFECTS += fft_pass_effect
 TESTED_EFFECTS += vignette_effect
+TESTED_EFFECTS += slice_effect
 
 UNTESTED_EFFECTS = sandbox_effect
 UNTESTED_EFFECTS += mirror_effect
index 46799e53af04d1bc714e3389bb00e296159c1de8..6036cf724c30cf97202ba7e9a1906663d679bb32 100644 (file)
@@ -24,7 +24,7 @@ FlatInput::FlatInput(ImageFormat image_format, MovitPixelFormat pixel_format, GL
          pitch(width),
          pixel_data(NULL)
 {
-       assert(type == GL_FLOAT || type == GL_UNSIGNED_BYTE);
+       assert(type == GL_FLOAT || type == GL_HALF_FLOAT || type == GL_UNSIGNED_BYTE);
        register_int("output_linear_gamma", &output_linear_gamma);
        register_int("needs_mipmaps", &needs_mipmaps);
 }
@@ -46,7 +46,17 @@ void FlatInput::set_gl_state(GLuint glsl_program_num, const string& prefix, unsi
                GLint internal_format;
                GLenum format;
                if (type == GL_FLOAT) {
-                       internal_format = GL_RGBA32F_ARB;
+                       if (pixel_format == FORMAT_RG) {
+                               internal_format = GL_RG32F;
+                       } else {
+                               internal_format = GL_RGBA32F;
+                       }
+               } else if (type == GL_HALF_FLOAT) {
+                       if (pixel_format == FORMAT_RG) {
+                               internal_format = GL_RG16F;
+                       } else {
+                               internal_format = GL_RGBA16F;
+                       }
                } else if (output_linear_gamma) {
                        assert(type == GL_UNSIGNED_BYTE);
                        internal_format = GL_SRGB8_ALPHA8;
@@ -66,6 +76,8 @@ void FlatInput::set_gl_state(GLuint glsl_program_num, const string& prefix, unsi
                        format = GL_BGRA;
                } else if (pixel_format == FORMAT_GRAYSCALE) {
                        format = GL_LUMINANCE;
+               } else if (pixel_format == FORMAT_RG) {
+                       format = GL_RG;
                } else {
                        assert(false);
                }
index c11d0a0123fcdb8bb4bd8bb4e60c26beed09692d..caa68b77900fcbff7e2db2ab7999e66f40d0d942 100644 (file)
@@ -7,6 +7,7 @@
 
 #include "effect.h"
 #include "effect_chain.h"
+#include "fp16.h"
 #include "image_format.h"
 #include "init.h"
 #include "input.h"
@@ -38,6 +39,7 @@ public:
                case FORMAT_RGBA_POSTMULTIPLIED_ALPHA:
                case FORMAT_BGRA_POSTMULTIPLIED_ALPHA:
                        return OUTPUT_POSTMULTIPLIED_ALPHA;
+               case FORMAT_RG:
                case FORMAT_RGB:
                case FORMAT_BGR:
                case FORMAT_GRAYSCALE:
@@ -75,6 +77,14 @@ public:
                invalidate_pixel_data();
        }
 
+       void set_pixel_data(const fp16_int_t *pixel_data, GLuint pbo = 0)
+       {
+               assert(this->type == GL_HALF_FLOAT);
+               this->pixel_data = pixel_data;
+               this->pbo = pbo;
+               invalidate_pixel_data();
+       }
+
        void set_pixel_data(const float *pixel_data, GLuint pbo = 0)
        {
                assert(this->type == GL_FLOAT);
index 77838ececa33918b061744e31b5f5cc58adbb9bf..54983bf8ef2a167105c303823502177174e62cad 100644 (file)
@@ -20,7 +20,8 @@ enum MovitPixelFormat {
        FORMAT_BGR,
        FORMAT_BGRA_PREMULTIPLIED_ALPHA,
        FORMAT_BGRA_POSTMULTIPLIED_ALPHA,
-       FORMAT_GRAYSCALE
+       FORMAT_GRAYSCALE,
+       FORMAT_RG
 };
 
 enum Colorspace {
diff --git a/slice_effect.cpp b/slice_effect.cpp
new file mode 100644 (file)
index 0000000..3cdd415
--- /dev/null
@@ -0,0 +1,76 @@
+#include <epoxy/gl.h>
+
+#include "slice_effect.h"
+#include "effect_util.h"
+#include "util.h"
+
+using namespace std;
+
+namespace movit {
+
+SliceEffect::SliceEffect()
+{
+       register_int("input_slice_size", &input_slice_size);
+       register_int("output_slice_size", &output_slice_size);
+       register_int("direction", (int *)&direction);
+}
+
+string SliceEffect::output_fragment_shader()
+{
+       char buf[256];
+       sprintf(buf, "#define DIRECTION_VERTICAL %d\n", (direction == VERTICAL));
+       return buf + read_file("slice_effect.frag");
+}
+       
+void SliceEffect::inform_input_size(unsigned input_num, unsigned width, unsigned height)
+{
+       assert(input_num == 0);
+       input_width = width;
+       input_height = height;
+}
+
+void SliceEffect::get_output_size(unsigned *width, unsigned *height,
+                                  unsigned *virtual_width, unsigned *virtual_height) const
+{
+       if (direction == HORIZONTAL) {
+               *width = div_round_up(input_width, input_slice_size) * output_slice_size;
+               *height = input_height; 
+       } else {
+               *width = input_width;   
+               *height = div_round_up(input_height, input_slice_size) * output_slice_size;
+       }
+       *virtual_width = *width;
+       *virtual_height = *height;
+}
+
+void SliceEffect::set_gl_state(GLuint glsl_program_num, const string &prefix, unsigned *sampler_num)
+{
+       Effect::set_gl_state(glsl_program_num, prefix, sampler_num);
+
+       unsigned output_width, output_height;
+       get_output_size(&output_width, &output_height, &output_width, &output_height);
+
+       if (direction == HORIZONTAL) {
+               set_uniform_float(glsl_program_num, prefix, "output_coord_to_slice_num", float(output_width) / float(output_slice_size));
+               set_uniform_float(glsl_program_num, prefix, "slice_num_to_input_coord", float(input_slice_size) / float(input_width));
+               set_uniform_float(glsl_program_num, prefix, "slice_offset_to_input_coord", float(output_slice_size) / float(input_width));
+       } else {
+               set_uniform_float(glsl_program_num, prefix, "output_coord_to_slice_num", float(output_height) / float(output_slice_size));
+               set_uniform_float(glsl_program_num, prefix, "slice_num_to_input_coord", float(input_slice_size) / float(input_height));
+               set_uniform_float(glsl_program_num, prefix, "slice_offset_to_input_coord", float(output_slice_size) / float(input_height));
+       }
+
+       // Normalized coordinates could potentially cause blurring of the
+       // image; it's not critical, but we have set changes_output_size()
+       // and needs_texture_bounce(), so simply turning off the interpolation
+       // is allowed.
+       assert(*sampler_num == 1);
+       glActiveTexture(GL_TEXTURE0);
+       check_error();
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+       check_error();
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+       check_error();
+}
+
+}  // namespace movit
diff --git a/slice_effect.frag b/slice_effect.frag
new file mode 100644 (file)
index 0000000..8416515
--- /dev/null
@@ -0,0 +1,26 @@
+uniform float PREFIX(output_coord_to_slice_num);
+uniform float PREFIX(slice_num_to_input_coord);
+uniform float PREFIX(slice_offset_to_input_coord);
+vec4 FUNCNAME(vec2 tc) {
+       // DIRECTION_VERTICAL will be #defined to 1 if we are expanding vertically,
+       // and 0 otherwise.
+#if DIRECTION_VERTICAL
+       float sliced_coord = tc.y;
+#else
+       float sliced_coord = tc.x;
+#endif
+
+       // Find out which slice we are in, and a 0..1 coordinate for the offset within that slice.
+       float slice_num = floor(sliced_coord * PREFIX(output_coord_to_slice_num));
+       float slice_offset = fract(sliced_coord * PREFIX(output_coord_to_slice_num));
+
+       // Find out where this slice begins in the input data, and then offset from that.
+       float input_coord = slice_num * PREFIX(slice_num_to_input_coord) + slice_offset * PREFIX(slice_offset_to_input_coord);
+
+#if DIRECTION_VERTICAL
+       return INPUT(vec2(tc.x, input_coord));
+#else
+       return INPUT(vec2(input_coord, tc.y));
+#endif
+}
diff --git a/slice_effect.h b/slice_effect.h
new file mode 100644 (file)
index 0000000..3448941
--- /dev/null
@@ -0,0 +1,43 @@
+#ifndef _MOVIT_SLICE_EFFECT_H
+#define _MOVIT_SLICE_EFFECT_H 1
+
+// SliceEffect takes an image, cuts it into (potentially overlapping) slices,
+// and puts those slices back together again consecutively. It is primarily
+// useful in an overlap-discard setting, where it can do both the overlap and
+// discard roles, where one does convolutions by means of many small FFTs, but
+// could also work as a (relatively boring) video effect on its own.
+//
+// Note that vertical slices happen from the bottom, not the top, due to the
+// OpenGL coordinate system.
+
+#include <epoxy/gl.h>
+#include <string>
+
+#include "effect.h"
+
+namespace movit {
+
+class SliceEffect : public Effect {
+public:
+       SliceEffect();
+       virtual std::string effect_type_id() const { return "SliceEffect"; }
+       std::string output_fragment_shader();
+       virtual bool needs_texture_bounce() const { return true; }
+       virtual bool changes_output_size() const { return true; }
+       virtual void inform_input_size(unsigned input_num, unsigned width, unsigned height);
+       virtual void get_output_size(unsigned *width, unsigned *height,
+                                    unsigned *virtual_width, unsigned *virtual_height) const;
+
+       void set_gl_state(GLuint glsl_program_num, const std::string &prefix, unsigned *sampler_num);
+       
+       enum Direction { HORIZONTAL = 0, VERTICAL = 1 };
+
+private:
+       int input_width, input_height;
+       int input_slice_size, output_slice_size;
+       Direction direction;
+};
+
+}  // namespace movit
+
+#endif // !defined(_MOVIT_SLICE_EFFECT_H)
diff --git a/slice_effect_test.cpp b/slice_effect_test.cpp
new file mode 100644 (file)
index 0000000..410439a
--- /dev/null
@@ -0,0 +1,117 @@
+// Unit tests for SliceEffect.
+
+#include <epoxy/gl.h>
+
+#include "effect_chain.h"
+#include "flat_input.h"
+#include "gtest/gtest.h"
+#include "image_format.h"
+#include "input.h"
+#include "slice_effect.h"
+#include "test_util.h"
+
+namespace movit {
+
+TEST(SliceEffectTest, Identity) {
+       const int size = 3, output_size = 4;
+       float data[size * size] = {
+               0.0f, 0.1f, 0.2f,
+               0.4f, 0.3f, 0.8f,
+               0.5f, 0.2f, 0.1f,
+       };
+       float expected_data[output_size * size] = {
+               0.0f, 0.1f, 0.2f, 0.2f,
+               0.4f, 0.3f, 0.8f, 0.8f,
+               0.5f, 0.2f, 0.1f, 0.1f,
+       };
+       float out_data[output_size * size];
+
+       EffectChainTester tester(NULL, output_size, size, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR);
+       tester.add_input(data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, size, size);
+
+       Effect *slice_effect = tester.get_chain()->add_effect(new SliceEffect());
+       ASSERT_TRUE(slice_effect->set_int("input_slice_size", 2));
+       ASSERT_TRUE(slice_effect->set_int("output_slice_size", 2));
+       ASSERT_TRUE(slice_effect->set_int("direction", SliceEffect::HORIZONTAL));
+       tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
+
+       expect_equal(expected_data, out_data, output_size, size);
+}
+
+TEST(SliceEffectTest, HorizontalOverlap) {
+       float data[5 * 2] = {
+               0.0f, 0.1f,  0.2f, 0.3f,  0.4f,
+               0.4f, 0.3f,  0.2f, 0.1f,  0.0f,
+       };
+       float expected_data[9 * 2] = {
+               0.0f, 0.1f, 0.2f,  0.2f, 0.3f, 0.4f,  0.4f, 0.4f, 0.4f,
+               0.4f, 0.3f, 0.2f,  0.2f, 0.1f, 0.0f,  0.0f, 0.0f, 0.0f,
+       };
+       float out_data[9 * 2];
+
+       EffectChainTester tester(NULL, 9, 2, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR);
+       tester.add_input(data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, 5, 2);
+
+       Effect *slice_effect = tester.get_chain()->add_effect(new SliceEffect());
+       ASSERT_TRUE(slice_effect->set_int("input_slice_size", 2));
+       ASSERT_TRUE(slice_effect->set_int("output_slice_size", 3));
+       ASSERT_TRUE(slice_effect->set_int("direction", SliceEffect::HORIZONTAL));
+       tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
+
+       expect_equal(expected_data, out_data, 9, 2);
+}
+
+TEST(SliceEffectTest, HorizontalDiscard) {
+       float data[6 * 2] = {
+               0.0f, 0.1f, 0.2f,  0.2f, 0.3f, 0.4f,
+               0.4f, 0.3f, 0.2f,  0.2f, 0.1f, 0.0f,
+       };
+       float expected_data[4 * 2] = {
+               0.0f, 0.1f,  0.2f, 0.3f,
+               0.4f, 0.3f,  0.2f, 0.1f,
+       };
+       float out_data[4 * 2];
+
+       EffectChainTester tester(NULL, 4, 2, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR);
+       tester.add_input(data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, 6, 2);
+
+       Effect *slice_effect = tester.get_chain()->add_effect(new SliceEffect());
+       ASSERT_TRUE(slice_effect->set_int("input_slice_size", 3));
+       ASSERT_TRUE(slice_effect->set_int("output_slice_size", 2));
+       ASSERT_TRUE(slice_effect->set_int("direction", SliceEffect::HORIZONTAL));
+       tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
+
+       expect_equal(expected_data, out_data, 4, 2);
+}
+
+TEST(SliceEffectTest, VerticalOverlapSlicesFromBottom) {
+       float data[2 * 3] = {
+               0.0f, 0.1f,
+
+               0.4f, 0.3f,
+               0.6f, 0.2f,
+       };
+       float expected_data[2 * 6] = {
+               0.0f, 0.1f,
+               0.0f, 0.1f,
+               0.0f, 0.1f,
+
+               0.0f, 0.1f,
+               0.4f, 0.3f,
+               0.6f, 0.2f,
+       };
+       float out_data[2 * 6];
+
+       EffectChainTester tester(NULL, 2, 6, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR);
+       tester.add_input(data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, 2, 3);
+
+       Effect *slice_effect = tester.get_chain()->add_effect(new SliceEffect());
+       ASSERT_TRUE(slice_effect->set_int("input_slice_size", 2));
+       ASSERT_TRUE(slice_effect->set_int("output_slice_size", 3));
+       ASSERT_TRUE(slice_effect->set_int("direction", SliceEffect::VERTICAL));
+       tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
+
+       expect_equal(expected_data, out_data, 2, 6);
+}
+
+}  // namespace movit
index ea3a0110ce6e509da802e0d3d9a0eb9deaa1aaf2..ec9cbf947a5bcbd7b518677f44f279c7972df301 100644 (file)
@@ -85,25 +85,39 @@ EffectChainTester::~EffectChainTester()
        check_error();
 }
 
-Input *EffectChainTester::add_input(const float *data, MovitPixelFormat pixel_format, Colorspace color_space, GammaCurve gamma_curve)
+Input *EffectChainTester::add_input(const float *data, MovitPixelFormat pixel_format, Colorspace color_space, GammaCurve gamma_curve, int input_width, int input_height)
 {
        ImageFormat format;
        format.color_space = color_space;
        format.gamma_curve = gamma_curve;
 
-       FlatInput *input = new FlatInput(format, pixel_format, GL_FLOAT, width, height);
+       if (input_width == -1) {
+               input_width = width;
+       }
+       if (input_height == -1) {
+               input_height = height;
+       }
+
+       FlatInput *input = new FlatInput(format, pixel_format, GL_FLOAT, input_width, input_height);
        input->set_pixel_data(data);
        chain.add_input(input);
        return input;
 }
 
-Input *EffectChainTester::add_input(const unsigned char *data, MovitPixelFormat pixel_format, Colorspace color_space, GammaCurve gamma_curve)
+Input *EffectChainTester::add_input(const unsigned char *data, MovitPixelFormat pixel_format, Colorspace color_space, GammaCurve gamma_curve, int input_width, int input_height)
 {
        ImageFormat format;
        format.color_space = color_space;
        format.gamma_curve = gamma_curve;
 
-       FlatInput *input = new FlatInput(format, pixel_format, GL_UNSIGNED_BYTE, width, height);
+       if (input_width == -1) {
+               input_width = width;
+       }
+       if (input_height == -1) {
+               input_height = height;
+       }
+
+       FlatInput *input = new FlatInput(format, pixel_format, GL_UNSIGNED_BYTE, input_width, input_height);
        input->set_pixel_data(data);
        chain.add_input(input);
        return input;
index b44d326d8d51a3c57390f7c8968efa541d9802bd..e5e65513ddb82914758162080fabae40e039a7de 100644 (file)
@@ -19,8 +19,8 @@ public:
        ~EffectChainTester();
        
        EffectChain *get_chain() { return &chain; }
-       Input *add_input(const float *data, MovitPixelFormat pixel_format, Colorspace color_space, GammaCurve gamma_curve);
-       Input *add_input(const unsigned char *data, MovitPixelFormat pixel_format, Colorspace color_space, GammaCurve gamma_curve);
+       Input *add_input(const float *data, MovitPixelFormat pixel_format, Colorspace color_space, GammaCurve gamma_curve, int input_width = -1, int input_height = -1);
+       Input *add_input(const unsigned char *data, MovitPixelFormat pixel_format, Colorspace color_space, GammaCurve gamma_curve, int input_width = -1, int input_height = -1);
        void run(float *out_data, GLenum format, Colorspace color_space, GammaCurve gamma_curve, OutputAlphaFormat alpha_format = OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED);
        void run(unsigned char *out_data, GLenum format, Colorspace color_space, GammaCurve gamma_curve, OutputAlphaFormat alpha_format = OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED);
 
index cd820bc74afe3d7ee8314ee1d47da82bb33cf55a..372064b28948238c1c30815f7cb9cdb82087128f 100644 (file)
--- a/util.cpp
+++ b/util.cpp
@@ -215,4 +215,9 @@ void cleanup_vertex_attribute(GLuint glsl_program_num, const string &attribute_n
        check_error();
 }
 
+unsigned div_round_up(unsigned a, unsigned b)
+{
+       return (a + b - 1) / b;
+}
+
 }  // namespace movit
diff --git a/util.h b/util.h
index 77841023363b862a834c86ee9367b28038650b4e..5c15c9f789fceda67e764002413d053911524806 100644 (file)
--- a/util.h
+++ b/util.h
@@ -34,6 +34,9 @@ void print_3x3_matrix(const Eigen::Matrix3d &m);
 // Output a GLSL 3x3 matrix declaration.
 std::string output_glsl_mat3(const std::string &name, const Eigen::Matrix3d &m);
 
+// Calculate a / b, rounding up. Does not handle overflow correctly.
+unsigned div_round_up(unsigned a, unsigned b);
+
 // Calculate where to sample, and with what weight, if one wants to use
 // the GPU's bilinear hardware to sample w1 * x[0] + w2 * x[1].
 //