+
+void EffectChain::add_effect_raw(Effect *effect, const std::vector<Effect *> &inputs)
+{
+ char effect_id[256];
+ sprintf(effect_id, "eff%u", (unsigned)effects.size());
+
+ effects.push_back(effect);
+ effect_ids.insert(std::make_pair(effect, effect_id));
+ assert(inputs.size() == effect->num_inputs());
+ for (unsigned i = 0; i < inputs.size(); ++i) {
+ assert(std::find(effects.begin(), effects.end(), inputs[i]) != effects.end());
+ outgoing_links[inputs[i]].push_back(effect);
+ }
+ incoming_links.insert(std::make_pair(effect, inputs));
+ output_gamma_curve[effect] = output_gamma_curve[last_added_effect()];
+ output_color_space[effect] = output_color_space[last_added_effect()];
+}
+
+void EffectChain::find_all_nonlinear_inputs(Effect *effect,
+ std::vector<Input *> *nonlinear_inputs,
+ std::vector<Effect *> *intermediates)
+{
+ assert(output_gamma_curve.count(effect) != 0);
+ if (output_gamma_curve[effect] == GAMMA_LINEAR) {
+ return;
+ }
+ if (effect->num_inputs() == 0) {
+ nonlinear_inputs->push_back(static_cast<Input *>(effect));
+ } else {
+ intermediates->push_back(effect);
+
+ assert(incoming_links.count(effect) == 1);
+ std::vector<Effect *> deps = incoming_links[effect];
+ assert(effect->num_inputs() == deps.size());
+ for (unsigned i = 0; i < deps.size(); ++i) {
+ find_all_nonlinear_inputs(deps[i], nonlinear_inputs, intermediates);
+ }
+ }
+}
+
+Effect *EffectChain::normalize_to_linear_gamma(Effect *input)
+{
+ // Find out if all the inputs can be set to deliver sRGB inputs.
+ // If so, we can just ask them to do that instead of inserting a
+ // (possibly expensive) conversion operation.
+ //
+ // NOTE: We assume that effects generally don't mess with the gamma
+ // curve (except GammaCompressionEffect, which should never be
+ // inserted into a chain when this is called), so that we can just
+ // update the output gamma as we go.
+ //
+ // TODO: Setting this flag for one source might confuse a different
+ // part of the pipeline using the same source.
+ std::vector<Input *> nonlinear_inputs;
+ std::vector<Effect *> intermediates;
+ find_all_nonlinear_inputs(input, &nonlinear_inputs, &intermediates);
+
+ bool all_ok = true;
+ for (unsigned i = 0; i < nonlinear_inputs.size(); ++i) {
+ all_ok &= nonlinear_inputs[i]->can_output_linear_gamma();
+ }
+
+ if (all_ok) {
+ for (unsigned i = 0; i < nonlinear_inputs.size(); ++i) {
+ bool ok = nonlinear_inputs[i]->set_int("output_linear_gamma", 1);
+ assert(ok);
+ output_gamma_curve[nonlinear_inputs[i]] = GAMMA_LINEAR;
+ }
+ for (unsigned i = 0; i < intermediates.size(); ++i) {
+ output_gamma_curve[intermediates[i]] = GAMMA_LINEAR;
+ }
+ return input;
+ }
+
+ // OK, that didn't work. Insert a conversion effect.
+ GammaExpansionEffect *gamma_conversion = new GammaExpansionEffect();
+ gamma_conversion->set_int("source_curve", output_gamma_curve[input]);
+ std::vector<Effect *> inputs;
+ inputs.push_back(input);
+ gamma_conversion->add_self_to_effect_chain(this, inputs);
+ output_gamma_curve[gamma_conversion] = GAMMA_LINEAR;
+ return gamma_conversion;
+}
+
+Effect *EffectChain::normalize_to_srgb(Effect *input)
+{
+ assert(output_gamma_curve.count(input) != 0);
+ assert(output_color_space.count(input) != 0);
+ assert(output_gamma_curve[input] == GAMMA_LINEAR);
+ ColorSpaceConversionEffect *colorspace_conversion = new ColorSpaceConversionEffect();
+ colorspace_conversion->set_int("source_space", output_color_space[input]);
+ colorspace_conversion->set_int("destination_space", COLORSPACE_sRGB);
+ std::vector<Effect *> inputs;
+ inputs.push_back(input);
+ colorspace_conversion->add_self_to_effect_chain(this, inputs);
+ output_color_space[colorspace_conversion] = COLORSPACE_sRGB;
+ return colorspace_conversion;
+}
+
+Effect *EffectChain::add_effect(Effect *effect, const std::vector<Effect *> &inputs)
+{
+ assert(inputs.size() == effect->num_inputs());
+
+ std::vector<Effect *> normalized_inputs = inputs;
+ for (unsigned i = 0; i < normalized_inputs.size(); ++i) {
+ assert(output_gamma_curve.count(normalized_inputs[i]) != 0);
+ if (effect->needs_linear_light() && output_gamma_curve[normalized_inputs[i]] != GAMMA_LINEAR) {
+ normalized_inputs[i] = normalize_to_linear_gamma(normalized_inputs[i]);
+ }
+ assert(output_color_space.count(normalized_inputs[i]) != 0);
+ if (effect->needs_srgb_primaries() && output_color_space[normalized_inputs[i]] != COLORSPACE_sRGB) {
+ normalized_inputs[i] = normalize_to_srgb(normalized_inputs[i]);
+ }
+ }
+
+ effect->add_self_to_effect_chain(this, normalized_inputs);
+ return effect;
+}
+
+// GLSL pre-1.30 doesn't support token pasting. Replace PREFIX(x) with <effect_id>_x.
+std::string replace_prefix(const std::string &text, const std::string &prefix)
+{
+ std::string output;
+ size_t start = 0;
+
+ while (start < text.size()) {
+ size_t pos = text.find("PREFIX(", start);
+ if (pos == std::string::npos) {
+ output.append(text.substr(start, std::string::npos));
+ break;
+ }
+
+ output.append(text.substr(start, pos - start));
+ output.append(prefix);
+ output.append("_");
+
+ pos += strlen("PREFIX(");