Add an effect for padding.
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Thu, 17 Jan 2013 00:50:36 +0000 (01:50 +0100)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Thu, 17 Jan 2013 00:50:36 +0000 (01:50 +0100)
Makefile
padding_effect.cpp [new file with mode: 0644]
padding_effect.frag [new file with mode: 0644]
padding_effect.h [new file with mode: 0644]

index b9b66f4..306a730 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -45,6 +45,7 @@ TESTS += diffusion_effect_test
 TESTS += white_balance_effect_test
 TESTS += lift_gamma_gain_effect_test
 TESTS += resample_effect_test
+TESTS += padding_effect_test
 TESTS += dither_effect_test
 TESTS += flat_input_test
 TESTS += ycbcr_input_test
@@ -74,6 +75,7 @@ LIB_OBJS += unsharp_mask_effect.o
 LIB_OBJS += mix_effect.o
 LIB_OBJS += overlay_effect.o
 LIB_OBJS += resize_effect.o
+LIB_OBJS += padding_effect.o
 LIB_OBJS += resample_effect.o
 LIB_OBJS += dither_effect.o
 LIB_OBJS += deconvolution_sharpen_effect.o
diff --git a/padding_effect.cpp b/padding_effect.cpp
new file mode 100644 (file)
index 0000000..9d6e292
--- /dev/null
@@ -0,0 +1,113 @@
+#include <math.h>
+#include <GL/glew.h>
+
+#include "padding_effect.h"
+#include "util.h"
+
+PaddingEffect::PaddingEffect()
+       : border_color(0.0f, 0.0f, 0.0f, 0.0f),
+         output_width(1280),
+         output_height(720),
+         top(0),
+         left(0)
+{
+       register_vec4("border_color", (float *)&border_color);
+       register_int("width", &output_width);
+       register_int("height", &output_height);
+       register_float("top", &top);
+       register_float("left", &left);
+}
+
+std::string PaddingEffect::output_fragment_shader()
+{
+       return read_file("padding_effect.frag");
+}
+
+void PaddingEffect::set_gl_state(GLuint glsl_program_num, const std::string &prefix, unsigned *sampler_num)
+{
+       Effect::set_gl_state(glsl_program_num, prefix, sampler_num);
+
+       float offset[2] = {
+               left / output_width,
+               (output_height - input_height - top) / output_height
+       };
+       set_uniform_vec2(glsl_program_num, prefix, "offset", offset);
+
+       float scale[2] = {
+               float(output_width) / input_width,
+               float(output_height) / input_height
+       };
+       set_uniform_vec2(glsl_program_num, prefix, "scale", scale);
+
+       // Due to roundoff errors, the test against 0.5 is seldom exact,
+       // even though we test for less than and not less-than-or-equal.
+       // We'd rather keep an extra border pixel in those very rare cases
+       // (where the image is shifted pretty much exactly a half-pixel)
+       // than losing a pixel in the common cases of integer shift.
+       // Thus the 1e-3 fudge factors.
+       float texcoord_min[2] = {
+               (0.5f - 1e-3) / input_width,
+               (0.5f - 1e-3) / input_height
+       };
+       set_uniform_vec2(glsl_program_num, prefix, "texcoord_min", texcoord_min);
+
+       float texcoord_max[2] = {
+               1.0f - (0.5f - 1e-3) / input_width,
+               1.0f - (0.5f - 1e-3) / input_height
+       };
+       set_uniform_vec2(glsl_program_num, prefix, "texcoord_max", texcoord_max);
+}
+       
+// We don't change the pixels of the image itself, so the only thing that 
+// can make us less flexible is if the border color can be interpreted
+// differently in different modes.
+
+// 0.0 and 1.0 are interpreted the same, no matter the gamma ramp.
+// Alpha is not affected by gamma.
+bool PaddingEffect::needs_linear_light() const
+{
+       if ((border_color.r == 0.0 || border_color.r == 1.0) &&
+           (border_color.g == 0.0 || border_color.g == 1.0) &&
+           (border_color.b == 0.0 || border_color.b == 1.0)) {
+               return false;
+       }
+       return true;
+}
+
+// The white point is the same (D65) in all the color spaces we currently support,
+// so any gray would be okay, but we don't really have a guarantee for that.
+// Stay safe and say that only pure black and pure white is okay.
+// Alpha is not affected by color space.
+bool PaddingEffect::needs_srgb_primaries() const
+{
+       if (border_color.r == 0.0 && border_color.g == 0.0 && border_color.b == 0.0) {
+               return false;
+       }
+       if (border_color.r == 1.0 && border_color.g == 1.0 && border_color.b == 1.0) {
+               return false;
+       }
+       return true;
+}
+
+// If the border color is black, it doesn't matter if we're pre- or postmultiplied
+// (or even blank, as a hack). Otherwise, it does.
+Effect::AlphaHandling PaddingEffect::alpha_handling() const
+{
+       if (border_color.r == 0.0 && border_color.g == 0.0 && border_color.b == 0.0) {
+               return DONT_CARE_ALPHA_TYPE;
+       }
+       return INPUT_AND_OUTPUT_ALPHA_PREMULTIPLIED;
+}
+       
+void PaddingEffect::get_output_size(unsigned *width, unsigned *height) const
+{
+       *width = output_width;
+       *height = output_height;
+}
+       
+void PaddingEffect::inform_input_size(unsigned input_num, unsigned width, unsigned height)
+{
+       assert(input_num == 0);
+       input_width = width;
+       input_height = height;
+}
diff --git a/padding_effect.frag b/padding_effect.frag
new file mode 100644 (file)
index 0000000..344b34f
--- /dev/null
@@ -0,0 +1,30 @@
+uniform vec2 PREFIX(offset);
+uniform vec2 PREFIX(scale);
+uniform vec2 PREFIX(texcoord_min);
+uniform vec2 PREFIX(texcoord_max);
+
+vec4 FUNCNAME(vec2 tc) {
+       tc -= PREFIX(offset);
+       tc *= PREFIX(scale);
+
+#if 1
+       if (any(lessThan(tc, PREFIX(texcoord_min))) ||
+           any(greaterThan(tc, PREFIX(texcoord_max)))) {
+               return PREFIX(border_color);
+       }
+#endif
+       if (any(lessThan(tc, vec2(0.0))) ||
+           any(greaterThan(tc, vec2(1.0)))) {
+               return PREFIX(border_color);
+       } 
+#if 0
+       // In theory, maybe we should test on the outmost textel centers
+       // (e.g. (0.5/width, 0.5/height) for the top-left) instead of the
+       // texture border. However, since we only support integer padding
+       if (any(lessThan(tc, vec2(0.0))) || any(greaterThan(tc, vec2(1.0)))) {
+               return PREFIX(border_color);
+       }
+#endif
+
+       return INPUT(tc);
+}
diff --git a/padding_effect.h b/padding_effect.h
new file mode 100644 (file)
index 0000000..e9d4ce7
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef _PADDING_EFFECT_H
+#define _PADDING_EFFECT_H 1
+
+// Takes an image and pads it to fit a larger image, or crops it to fit a smaller one
+// (although the latter is implemented slightly less efficiently, and you cannot both
+// pad and crop in the same effect).
+//
+// The source image is cut off at the texel borders (so there is no interpolation
+// outside them), and then given a user-specific color; by default, full transparent.
+//
+// The border color is taken to be in linear gamma, sRGB, with premultiplied alpha.
+// You may not change it after calling finalize(), since that could change the
+// graph (need_linear_light() etc. depend on the border color you choose).
+
+#include "effect.h"
+
+class PaddingEffect : public Effect {
+public:
+       PaddingEffect();
+       virtual std::string effect_type_id() const { return "PaddingEffect"; }
+       std::string output_fragment_shader();
+       void set_gl_state(GLuint glsl_program_num, const std::string &prefix, unsigned *sampler_num);
+
+       virtual bool needs_linear_light() const;
+       virtual bool needs_srgb_primaries() const;
+       virtual AlphaHandling alpha_handling() const;
+       
+       virtual bool changes_output_size() const { return true; }
+       virtual void get_output_size(unsigned *width, unsigned *height) const;
+       virtual void inform_input_size(unsigned input_num, unsigned width, unsigned height);
+
+private:
+       RGBATriplet border_color;
+       int input_width, input_height;
+       int output_width, output_height;
+       float top, left;
+};
+
+#endif // !defined(_PADDING_EFFECT_H)