From: Steinar H. Gunderson Date: Fri, 1 Feb 2013 00:03:00 +0000 (+0100) Subject: In resizing effects, add the notion of a “virtual output size”. X-Git-Tag: 1.0~144 X-Git-Url: https://git.sesse.net/?p=movit;a=commitdiff_plain;h=2322070a3dbeb6b46b39cca07a0fbf20e95f5468 In resizing effects, add the notion of a “virtual output size”. This is the size that the effect wants to be perceived as for the next effect in the chain, which is useful if you e.g. have a blur and then padding. Even though the blur might rescale the image down from e.g. 512x512 to 128x128 before blurring (in case of a large blur), and this size is what actually comes out in the texture at the other end, it still wants to be treated as a 512x512 image when adding padding. Reported by Christophe Thommeret. --- diff --git a/blur_effect.cpp b/blur_effect.cpp index 7723853..d62b056 100644 --- a/blur_effect.cpp +++ b/blur_effect.cpp @@ -64,10 +64,14 @@ void BlurEffect::update_radius() bool ok = hpass->set_float("radius", adjusted_radius); ok |= hpass->set_int("width", mipmap_width); ok |= hpass->set_int("height", mipmap_height); + ok |= hpass->set_int("virtual_width", mipmap_width); + ok |= hpass->set_int("virtual_height", mipmap_height); ok |= vpass->set_float("radius", adjusted_radius); ok |= vpass->set_int("width", mipmap_width); ok |= vpass->set_int("height", mipmap_height); + ok |= vpass->set_int("virtual_width", input_width); + ok |= vpass->set_int("virtual_height", input_height); assert(ok); } @@ -92,6 +96,8 @@ SingleBlurPassEffect::SingleBlurPassEffect(BlurEffect *parent) register_int("direction", (int *)&direction); register_int("width", &width); register_int("height", &height); + register_int("virtual_width", &virtual_width); + register_int("virtual_height", &virtual_height); } std::string SingleBlurPassEffect::output_fragment_shader() diff --git a/blur_effect.h b/blur_effect.h index b680947..792014b 100644 --- a/blur_effect.h +++ b/blur_effect.h @@ -66,9 +66,11 @@ public: } virtual bool changes_output_size() const { return true; } - virtual void get_output_size(unsigned *width, unsigned *height) const { + virtual void get_output_size(unsigned *width, unsigned *height, unsigned *virtual_width, unsigned *virtual_height) const { *width = this->width; *height = this->height; + *virtual_width = this->virtual_width; + *virtual_height = this->virtual_height; } void set_gl_state(GLuint glsl_program_num, const std::string &prefix, unsigned *sampler_num); @@ -80,7 +82,7 @@ private: BlurEffect *parent; float radius; Direction direction; - int width, height; + int width, height, virtual_width, virtual_height; }; #endif // !defined(_BLUR_EFFECT_H) diff --git a/effect.h b/effect.h index d7db299..c911f27 100644 --- a/effect.h +++ b/effect.h @@ -168,11 +168,16 @@ public: virtual bool changes_output_size() const { return false; } // If changes_output_size() is true, you must implement this to tell - // the framework what output size you want. + // the framework what output size you want. Also, you can set a + // virtual width/height, which is the size the next effect (if any) + // will _think_ your data is in. This is primarily useful if you are + // relying on getting OpenGL's bilinear resizing for free; otherwise, + // your virtual_width/virtual_height should be the same as width/height. // // Note that it is explicitly allowed to change width and height // from frame to frame; EffectChain will reallocate textures as needed. - virtual void get_output_size(unsigned *width, unsigned *height) const { + virtual void get_output_size(unsigned *width, unsigned *height, + unsigned *virtual_width, unsigned *virtual_height) const { assert(false); } diff --git a/effect_chain.cpp b/effect_chain.cpp index 9aaa748..8a7ea9a 100644 --- a/effect_chain.cpp +++ b/effect_chain.cpp @@ -618,8 +618,8 @@ void EffectChain::inform_input_sizes(Phase *phase) } for (unsigned i = 0; i < phase->inputs.size(); ++i) { Node *input = phase->inputs[i]; - input->output_width = input->phase->output_width; - input->output_height = input->phase->output_height; + input->output_width = input->phase->virtual_output_width; + input->output_height = input->phase->virtual_output_height; assert(input->output_width != 0); assert(input->output_height != 0); } @@ -662,7 +662,8 @@ void EffectChain::find_output_size(Phase *phase) // If the last effect explicitly sets an output size, use that. if (output_node->effect->changes_output_size()) { - output_node->effect->get_output_size(&phase->output_width, &phase->output_height); + output_node->effect->get_output_size(&phase->output_width, &phase->output_height, + &phase->virtual_output_width, &phase->virtual_output_height); return; } @@ -675,10 +676,10 @@ void EffectChain::find_output_size(Phase *phase) assert(input->phase->output_width != 0); assert(input->phase->output_height != 0); if (output_width == 0 && output_height == 0) { - output_width = input->phase->output_width; - output_height = input->phase->output_height; - } else if (output_width != input->phase->output_width || - output_height != input->phase->output_height) { + output_width = input->phase->virtual_output_width; + output_height = input->phase->virtual_output_height; + } else if (output_width != input->phase->virtual_output_width || + output_height != input->phase->virtual_output_height) { all_inputs_same_size = false; } } @@ -701,8 +702,8 @@ void EffectChain::find_output_size(Phase *phase) if (all_inputs_same_size) { assert(output_width != 0); assert(output_height != 0); - phase->output_width = output_width; - phase->output_height = output_height; + phase->virtual_output_width = phase->output_width = output_width; + phase->virtual_output_height = phase->output_height = output_height; return; } @@ -726,8 +727,8 @@ void EffectChain::find_output_size(Phase *phase) } assert(output_width != 0); assert(output_height != 0); - phase->output_width = output_width; - phase->output_height = output_height; + phase->virtual_output_width = phase->output_width = output_width; + phase->virtual_output_height = phase->output_height = output_height; } void EffectChain::sort_all_nodes_topologically() diff --git a/effect_chain.h b/effect_chain.h index 88d5d32..bfe52c0 100644 --- a/effect_chain.h +++ b/effect_chain.h @@ -72,7 +72,7 @@ struct Phase { std::vector inputs; std::vector effects; // In order. - unsigned output_width, output_height; + unsigned output_width, output_height, virtual_output_width, virtual_output_height; }; class EffectChain { diff --git a/effect_chain_test.cpp b/effect_chain_test.cpp index 51412c0..5fa81d3 100644 --- a/effect_chain_test.cpp +++ b/effect_chain_test.cpp @@ -847,3 +847,53 @@ TEST(EffectChainTest, AspectRatioConversion) { EXPECT_EQ(9, input_store->input_width); EXPECT_EQ(7, input_store->input_height); } + +// An effect that does nothing except changing its output sizes. +class VirtualResizeEffect : public Effect { +public: + VirtualResizeEffect(int width, int height, int virtual_width, int virtual_height) + : width(width), + height(height), + virtual_width(virtual_width), + virtual_height(virtual_height) {} + virtual std::string effect_type_id() const { return "VirtualResizeEffect"; } + std::string output_fragment_shader() { return read_file("identity.frag"); } + + virtual bool changes_output_size() const { return true; } + + virtual void get_output_size(unsigned *width, unsigned *height, + unsigned *virtual_width, unsigned *virtual_height) const { + *width = this->width; + *height = this->height; + *virtual_width = this->virtual_width; + *virtual_height = this->virtual_height; + } + +private: + int width, height, virtual_width, virtual_height; +}; + +TEST(EffectChainTest, VirtualSizeIsSentOnToInputs) { + const int size = 2, bigger_size = 3; + float data[size * size] = { + 1.0f, 0.0f, + 0.0f, 1.0f, + }; + float out_data[size * size]; + + EffectChainTester tester(data, size, size, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR); + + SizeStoringEffect *size_store = new SizeStoringEffect(); + + tester.get_chain()->add_effect(new VirtualResizeEffect(size, size, bigger_size, bigger_size)); + tester.get_chain()->add_effect(size_store); + tester.get_chain()->add_effect(new VirtualResizeEffect(size, size, size, size)); + tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR); + + EXPECT_EQ(bigger_size, size_store->input_width); + EXPECT_EQ(bigger_size, size_store->input_height); + + // If the resize is implemented as non-virtual, we'll fail here, + // since bilinear scaling from 2x2 → 3x3 → 2x2 is not very exact. + expect_equal(data, out_data, size, size); +} diff --git a/padding_effect.cpp b/padding_effect.cpp index 2e428c4..c06a1ac 100644 --- a/padding_effect.cpp +++ b/padding_effect.cpp @@ -99,10 +99,10 @@ Effect::AlphaHandling PaddingEffect::alpha_handling() const return INPUT_AND_OUTPUT_ALPHA_PREMULTIPLIED; } -void PaddingEffect::get_output_size(unsigned *width, unsigned *height) const +void PaddingEffect::get_output_size(unsigned *width, unsigned *height, unsigned *virtual_width, unsigned *virtual_height) const { - *width = output_width; - *height = output_height; + *virtual_width = *width = output_width; + *virtual_height = *height = output_height; } void PaddingEffect::inform_input_size(unsigned input_num, unsigned width, unsigned height) diff --git a/padding_effect.h b/padding_effect.h index e9d4ce7..9e206ce 100644 --- a/padding_effect.h +++ b/padding_effect.h @@ -26,7 +26,7 @@ public: 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 get_output_size(unsigned *width, unsigned *height, unsigned *virtual_width, unsigned *virtual_height) const; virtual void inform_input_size(unsigned input_num, unsigned width, unsigned height); private: diff --git a/resample_effect.h b/resample_effect.h index 5867f25..a0ae1a8 100644 --- a/resample_effect.h +++ b/resample_effect.h @@ -70,9 +70,9 @@ public: } virtual bool changes_output_size() const { return true; } - virtual void get_output_size(unsigned *width, unsigned *height) const { - *width = this->output_width; - *height = this->output_height; + virtual void get_output_size(unsigned *width, unsigned *height, unsigned *virtual_width, unsigned *virtual_height) const { + *virtual_width = *width = this->output_width; + *virtual_height = *height = this->output_height; } void set_gl_state(GLuint glsl_program_num, const std::string &prefix, unsigned *sampler_num); diff --git a/resize_effect.cpp b/resize_effect.cpp index e701d71..1141fba 100644 --- a/resize_effect.cpp +++ b/resize_effect.cpp @@ -13,8 +13,8 @@ std::string ResizeEffect::output_fragment_shader() return read_file("identity.frag"); } -void ResizeEffect::get_output_size(unsigned *width, unsigned *height) const +void ResizeEffect::get_output_size(unsigned *width, unsigned *height, unsigned *virtual_width, unsigned *virtual_height) const { - *width = this->width; - *height = this->height; + *virtual_width = *width = this->width; + *virtual_height = *height = this->height; } diff --git a/resize_effect.h b/resize_effect.h index 4c3fe86..c11f4bc 100644 --- a/resize_effect.h +++ b/resize_effect.h @@ -19,7 +19,7 @@ public: virtual bool needs_mipmaps() const { return true; } virtual bool changes_output_size() const { return true; } - virtual void get_output_size(unsigned *width, unsigned *height) const; + virtual void get_output_size(unsigned *width, unsigned *height, unsigned *virtual_width, unsigned *virtual_height) const; private: int width, height;