From eddc371976d7586176a74503ec9896159ececa43 Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Thu, 17 Jan 2013 01:50:36 +0100 Subject: [PATCH] Add an effect for padding. --- Makefile | 2 + padding_effect.cpp | 113 ++++++++++++++++++++++++++++++++++++++++++++ padding_effect.frag | 30 ++++++++++++ padding_effect.h | 39 +++++++++++++++ 4 files changed, 184 insertions(+) create mode 100644 padding_effect.cpp create mode 100644 padding_effect.frag create mode 100644 padding_effect.h diff --git a/Makefile b/Makefile index b9b66f4..306a730 100644 --- 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 index 0000000..9d6e292 --- /dev/null +++ b/padding_effect.cpp @@ -0,0 +1,113 @@ +#include +#include + +#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 index 0000000..344b34f --- /dev/null +++ b/padding_effect.frag @@ -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 index 0000000..e9d4ce7 --- /dev/null +++ b/padding_effect.h @@ -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) -- 2.39.2