Pull EffectChain a step closer to input resolution independence.
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Mon, 8 Oct 2012 22:53:36 +0000 (00:53 +0200)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Mon, 8 Oct 2012 22:53:36 +0000 (00:53 +0200)
effect_chain.cpp
effect_chain.h
flat_input.h
input.h
main.cpp
ycbcr_input.h

index b04727b..87d0663 100644 (file)
@@ -1,6 +1,7 @@
 #define GL_GLEXT_PROTOTYPES 1
 
 #include <stdio.h>
+#include <math.h>
 #include <string.h>
 #include <assert.h>
 
@@ -17,9 +18,9 @@
 #include "input.h"
 #include "opengl.h"
 
-EffectChain::EffectChain(unsigned width, unsigned height)
-       : width(width),
-         height(height),
+EffectChain::EffectChain(float aspect_nom, float aspect_denom)
+       : aspect_nom(aspect_nom),
+         aspect_denom(aspect_denom),
          finalized(false) {}
 
 Input *EffectChain::add_input(Input *input)
@@ -436,6 +437,19 @@ void EffectChain::output_dot(const char *filename)
        fclose(fp);
 }
 
+unsigned EffectChain::fit_rectangle_to_aspect(unsigned width, unsigned height)
+{
+       if (float(width) * aspect_denom >= float(height) * aspect_nom) {
+               // Same aspect, or W/H > aspect (image is wider than the frame).
+               // In either case, keep width.
+               return width;
+       } else {
+               // W/H < aspect (image is taller than the frame), so keep height,
+               // and adjust width correspondingly.
+               return lrintf(height * aspect_nom / aspect_denom);
+       }
+}
+
 void EffectChain::find_output_size(Phase *phase)
 {
        Node *output_node = phase->effects.back();
@@ -447,30 +461,33 @@ void EffectChain::find_output_size(Phase *phase)
                return;
        }
 
-       // If not, look at the input phases, if any. We select the largest one
-       // (really assuming they all have the same aspect currently), by pixel count.
-       if (!phase->inputs.empty()) {
-               unsigned best_width = 0, best_height = 0;
-               for (unsigned i = 0; i < phase->inputs.size(); ++i) {
-                       Node *input = phase->inputs[i];
-                       assert(input->phase->output_width != 0);
-                       assert(input->phase->output_height != 0);
-                       if (input->phase->output_width * input->phase->output_height > best_width * best_height) {
-                               best_width = input->phase->output_width;
-                               best_height = input->phase->output_height;
-                       }
+       // If not, look at the input phases and textures.
+       // We select the largest one (by fit into the current aspect).
+       unsigned best_width = 0;
+       for (unsigned i = 0; i < phase->inputs.size(); ++i) {
+               Node *input = phase->inputs[i];
+               assert(input->phase->output_width != 0);
+               assert(input->phase->output_height != 0);
+               unsigned width = fit_rectangle_to_aspect(input->phase->output_width, input->phase->output_height);
+               if (width > best_width) {
+                       best_width = width;
                }
-               assert(best_width != 0);
-               assert(best_height != 0);
-               phase->output_width = best_width;
-               phase->output_height = best_height;
-               return;
        }
+       for (unsigned i = 0; i < phase->effects.size(); ++i) {
+               Effect *effect = phase->effects[i]->effect;
+               if (effect->num_inputs() != 0) {
+                       continue;
+               }
 
-       // OK, no inputs. Just use the global width/height.
-       // TODO: We probably want to use the texture's size eventually.
-       phase->output_width = width;
-       phase->output_height = height;
+               Input *input = static_cast<Input *>(effect);
+               unsigned width = fit_rectangle_to_aspect(input->get_width(), input->get_height());
+               if (width > best_width) {
+                       best_width = width;
+               }
+       }
+       assert(best_width != 0);
+       phase->output_width = best_width;
+       phase->output_height = best_width * aspect_denom / aspect_nom;
 }
 
 void EffectChain::sort_nodes_topologically()
@@ -858,6 +875,10 @@ void EffectChain::render_to_screen()
 {
        assert(finalized);
 
+       // Save original viewport.
+       GLint viewport[4];
+       glGetIntegerv(GL_VIEWPORT, viewport);
+
        // Basic state.
        glDisable(GL_BLEND);
        check_error();
@@ -936,7 +957,7 @@ void EffectChain::render_to_screen()
                        // Last phase goes directly to the screen.
                        glBindFramebuffer(GL_FRAMEBUFFER, 0);
                        check_error();
-                       glViewport(0, 0, width, height);
+                       glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
                } else {
                        Node *output_node = phases[phase]->effects.back();
                        glFramebufferTexture2D(
index cf96293..b3e03d5 100644 (file)
@@ -54,7 +54,7 @@ struct Phase {
 
 class EffectChain {
 public:
-       EffectChain(unsigned width, unsigned height);
+       EffectChain(float aspect_nom, float aspect_denom);  // E.g., 16.0f, 9.0f for 16:9.
 
        // User API:
        // input, effects, output, finalize need to come in that specific order.
@@ -107,6 +107,10 @@ public:
        void insert_node_between(Node *sender, Node *middle, Node *receiver);
 
 private:
+       // Fits a rectangle of the given size to the current aspect ratio
+       // (aspect_nom/aspect_denom) and returns the new width and height.
+       unsigned fit_rectangle_to_aspect(unsigned width, unsigned height);
+
        // Determine the preferred output size of a given phase.
        // Requires that all input phases (if any) already have output sizes set.
        void find_output_size(Phase *phase);
@@ -146,7 +150,7 @@ private:
        void fix_internal_gamma_by_inserting_nodes(unsigned step);
        void fix_output_gamma();
 
-       unsigned width, height;
+       float aspect_nom, aspect_denom;
        ImageFormat output_format;
 
        std::vector<Node *> nodes;
index 2e14488..90c0386 100644 (file)
@@ -27,6 +27,8 @@ public:
        // Uploads the texture if it has changed since last time.
        void set_gl_state(GLuint glsl_program_num, const std::string& prefix, unsigned *sampler_num);
 
+       unsigned get_width() const { return width; }
+       unsigned get_height() const { return height; }
        ColorSpace get_color_space() const { return image_format.color_space; }
        GammaCurve get_gamma_curve() const { return image_format.gamma_curve; }
 
diff --git a/input.h b/input.h
index 8687690..ef94add 100644 (file)
--- a/input.h
+++ b/input.h
@@ -29,6 +29,8 @@ public:
        // to activate it.)
        virtual bool can_output_linear_gamma() const = 0;
 
+       virtual unsigned get_width() const = 0;
+       virtual unsigned get_height() const = 0;
        virtual ColorSpace get_color_space() const = 0;
        virtual GammaCurve get_gamma_curve() const = 0;
 };
index 1fbb6a2..0340cac 100644 (file)
--- a/main.cpp
+++ b/main.cpp
@@ -166,7 +166,7 @@ int main(int argc, char **argv)
        inout_format.color_space = COLORSPACE_sRGB;
        inout_format.gamma_curve = GAMMA_sRGB;
 
-       FlatInput *input = new FlatInput(inout_format, FORMAT_BGRA, WIDTH, HEIGHT);
+       FlatInput *input = new FlatInput(inout_format, FORMAT_BGRA, img_w, img_h);
        chain.add_input(input);
        Effect *lift_gamma_gain_effect = chain.add_effect(new LiftGammaGainEffect());
        Effect *saturation_effect = chain.add_effect(new SaturationEffect());
index 43d1f1d..379279f 100644 (file)
@@ -45,6 +45,8 @@ public:
        // Uploads the texture if it has changed since last time.
        void set_gl_state(GLuint glsl_program_num, const std::string& prefix, unsigned *sampler_num);
 
+       unsigned get_width() const { return width; }
+       unsigned get_height() const { return height; }
        ColorSpace get_color_space() const { return image_format.color_space; }
        GammaCurve get_gamma_curve() const { return image_format.gamma_curve; }