]> git.sesse.net Git - movit/blob - effect_chain.cpp
Output convenience uniforms per effect. Also, do not rely on the preprocessor support...
[movit] / effect_chain.cpp
1 #define GL_GLEXT_PROTOTYPES 1
2
3 #include <stdio.h>
4 #include <string.h>
5 #include <assert.h>
6
7 #include <GL/gl.h>
8 #include <GL/glext.h>
9
10 #include "util.h"
11 #include "effect_chain.h"
12 #include "gamma_expansion_effect.h"
13 #include "lift_gamma_gain_effect.h"
14 #include "colorspace_conversion_effect.h"
15
16 EffectChain::EffectChain(unsigned width, unsigned height)
17         : width(width), height(height), finalized(false) {}
18
19 void EffectChain::add_input(const ImageFormat &format)
20 {
21         input_format = format;
22         current_color_space = format.color_space;
23         current_gamma_curve = format.gamma_curve;
24 }
25
26 void EffectChain::add_output(const ImageFormat &format)
27 {
28         output_format = format;
29 }
30         
31 Effect *instantiate_effect(EffectId effect)
32 {
33         switch (effect) {
34         case GAMMA_CONVERSION:
35                 return new GammaExpansionEffect();
36         case RGB_PRIMARIES_CONVERSION:
37                 return new GammaExpansionEffect();
38         case LIFT_GAMMA_GAIN:
39                 return new LiftGammaGainEffect();
40         }
41         assert(false);
42 }
43
44 Effect *EffectChain::add_effect(EffectId effect_id)
45 {
46         Effect *effect = instantiate_effect(effect_id);
47
48         if (effect->needs_linear_light() && current_gamma_curve != GAMMA_LINEAR) {
49                 GammaExpansionEffect *gamma_conversion = new GammaExpansionEffect();
50                 gamma_conversion->set_int("source_curve", current_gamma_curve);
51                 effects.push_back(gamma_conversion);
52                 current_gamma_curve = GAMMA_LINEAR;
53         }
54
55         if (effect->needs_srgb_primaries() && current_color_space != COLORSPACE_sRGB) {
56                 assert(current_gamma_curve == GAMMA_LINEAR);
57                 ColorSpaceConversionEffect *colorspace_conversion = new ColorSpaceConversionEffect();
58                 colorspace_conversion->set_int("source_space", current_color_space);
59                 colorspace_conversion->set_int("destination_space", COLORSPACE_sRGB);
60                 effects.push_back(colorspace_conversion);
61                 current_color_space = COLORSPACE_sRGB;
62         }
63
64         // not handled yet
65         assert(!effect->needs_many_samples());
66         assert(!effect->needs_mipmaps());
67
68         effects.push_back(effect);
69         return effect;
70 }
71
72 // GLSL pre-1.30 doesn't support token pasting. Replace PREFIX(x) with <effect_id>_x.
73 std::string replace_prefix(const std::string &text, const std::string &prefix)
74 {
75         std::string output;
76         size_t start = 0;
77
78         while (start < text.size()) {
79                 size_t pos = text.find("PREFIX(", start);
80                 if (pos == std::string::npos) {
81                         output.append(text.substr(start, std::string::npos));
82                         break;
83                 }
84
85                 output.append(text.substr(start, pos - start));
86                 output.append(prefix);
87                 output.append("_");
88
89                 pos += strlen("PREFIX(");
90         
91                 // Output stuff until we find the matching ), which we then eat.
92                 int depth = 1;
93                 size_t end_arg_pos = pos;
94                 while (end_arg_pos < text.size()) {
95                         if (text[end_arg_pos] == '(') {
96                                 ++depth;
97                         } else if (text[end_arg_pos] == ')') {
98                                 --depth;
99                                 if (depth == 0) {
100                                         break;
101                                 }
102                         }
103                         ++end_arg_pos;
104                 }
105                 output.append(text.substr(pos, end_arg_pos - pos));
106                 ++end_arg_pos;
107                 assert(depth == 0);
108                 start = end_arg_pos;
109         }
110         return output;
111 }
112
113 void EffectChain::finalize()
114 {
115         std::string frag_shader = read_file("header.glsl");
116
117         for (unsigned i = 0; i < effects.size(); ++i) {
118                 char effect_id[256];
119                 sprintf(effect_id, "eff%d", i);
120         
121                 frag_shader += "\n";
122                 frag_shader += std::string("#define FUNCNAME ") + effect_id + "\n";
123                 frag_shader += replace_prefix(effects[i]->output_convenience_uniforms(), effect_id);
124                 frag_shader += replace_prefix(effects[i]->output_glsl(), effect_id);
125                 frag_shader += "#undef PREFIX\n";
126                 frag_shader += "#undef FUNCNAME\n";
127                 frag_shader += std::string("#define LAST_INPUT ") + effect_id + "\n";
128                 frag_shader += "\n";
129         }
130         printf("%s\n", frag_shader.c_str());
131         
132         glsl_program_num = glCreateProgram();
133         GLhandleARB vs_obj = compile_shader(read_file("vs.glsl"), GL_VERTEX_SHADER);
134         GLhandleARB fs_obj = compile_shader(frag_shader, GL_FRAGMENT_SHADER);
135         glAttachObjectARB(glsl_program_num, vs_obj);
136         check_error();
137         glAttachObjectARB(glsl_program_num, fs_obj);
138         check_error();
139         glLinkProgram(glsl_program_num);
140         check_error();
141 }