datadir = @datadir@
top_builddir = @top_builddir@
with_demo_app = @with_demo_app@
+with_SDL2 = @with_SDL2@
CC=@CC@
CXX=@CXX@
-CXXFLAGS=-Wall @CXXFLAGS@ -I$(GTEST_DIR)/include @Eigen3_CFLAGS@ @GLEW_CFLAGS@
-LDFLAGS=@GLEW_LIBS@ @SDL_LIBS@ -lpthread
-DEMO_LDLIBS=@SDL_image_LIBS@ -lrt -lpthread @libpng_LIBS@
+CXXFLAGS=-Wall @CXXFLAGS@ -I$(GTEST_DIR)/include @SDL2_CFLAGS@ @SDL_CFLAGS@ @Eigen3_CFLAGS@ @epoxy_CFLAGS@
+ifeq ($(with_SDL2),yes)
+CXXFLAGS += -DHAVE_SDL2
+endif
+LDFLAGS=@epoxy_LIBS@ @SDL2_LIBS@ @SDL_LIBS@ -lpthread
+DEMO_LDLIBS=@SDL2_image_LIBS@ @SDL_image_LIBS@ -lrt -lpthread @libpng_LIBS@
SHELL=@SHELL@
LIBTOOL=@LIBTOOL@ --tag=CXX
RANLIB=ranlib
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
#include <string.h>
#include <assert.h>
-#include <GL/glew.h>
+#include <epoxy/gl.h>
#include "effect_util.h"
#include "flat_input.h"
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);
}
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;
format = GL_BGRA;
} else if (pixel_format == FORMAT_GRAYSCALE) {
format = GL_LUMINANCE;
+ } else if (pixel_format == FORMAT_RG) {
+ format = GL_RG;
} else {
assert(false);
}
#ifndef _MOVIT_FLAT_INPUT_H
#define _MOVIT_FLAT_INPUT_H 1
-#include <GL/glew.h>
+#include <epoxy/gl.h>
#include <assert.h>
#include <string>
#include "effect.h"
#include "effect_chain.h"
+ #include "fp16.h"
#include "image_format.h"
#include "init.h"
#include "input.h"
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:
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);
--- /dev/null
-#include <GL/glew.h>
++#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
--- /dev/null
-#include <GL/glew.h>
+ #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)
--- /dev/null
-#include <GL/glew.h>
+ // 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
#include <math.h>
#include <stdio.h>
#include <algorithm>
+#include <epoxy/gl.h>
+#include <gtest/gtest.h>
+#include <gtest/gtest-message.h>
#include "flat_input.h"
-#include "glew.h"
-#include "gtest/gtest.h"
-#include "gtest/gtest-message.h"
#include "init.h"
#include "resource_pool.h"
#include "test_util.h"
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;
#ifndef _MOVIT_TEST_UTIL_H
#define _MOVIT_TEST_UTIL_H 1
-#include <GL/glew.h>
+#include <epoxy/gl.h>
#include "effect_chain.h"
#include "image_format.h"
~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);
-#include <GL/glew.h>
+#include <epoxy/gl.h>
#include <assert.h>
#include <math.h>
#include <stdio.h>
check_error();
}
+ unsigned div_round_up(unsigned a, unsigned b)
+ {
+ return (a + b - 1) / b;
+ }
+
} // namespace movit
// Various utilities.
-#include <GL/glew.h>
+#include <epoxy/gl.h>
#include <stdio.h>
#include <stdlib.h>
#include <Eigen/Core>
// 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].
//