+ // See if all inputs can give us linear gamma. If not, leave it.
+ std::vector<Node *> nonlinear_inputs;
+ find_all_nonlinear_inputs(node, &nonlinear_inputs);
+ assert(!nonlinear_inputs.empty());
+
+ bool all_ok = true;
+ for (unsigned i = 0; i < nonlinear_inputs.size(); ++i) {
+ Input *input = static_cast<Input *>(nonlinear_inputs[i]->effect);
+ all_ok &= input->can_output_linear_gamma();
+ }
+
+ if (!all_ok) {
+ continue;
+ }
+
+ for (unsigned i = 0; i < nonlinear_inputs.size(); ++i) {
+ CHECK(nonlinear_inputs[i]->effect->set_int("output_linear_gamma", 1));
+ nonlinear_inputs[i]->output_gamma_curve = GAMMA_LINEAR;
+ }
+
+ // Re-sort topologically, and propagate the new information.
+ propagate_gamma_and_color_space();
+
+ found_any = true;
+ break;
+ }
+
+ char filename[256];
+ sprintf(filename, "step%u-gammafix-iter%u.dot", step, ++gamma_propagation_pass);
+ output_dot(filename);
+ assert(gamma_propagation_pass < 100);
+ } while (found_any);
+}
+
+void EffectChain::fix_internal_gamma_by_inserting_nodes(unsigned step)
+{
+ unsigned gamma_propagation_pass = 0;
+ bool found_any;
+ do {
+ found_any = false;
+ for (unsigned i = 0; i < nodes.size(); ++i) {
+ Node *node = nodes[i];
+ if (!node_needs_gamma_fix(node)) {
+ continue;
+ }
+
+ // Special case: We could be an input and still be asked to
+ // fix our gamma; if so, we should be the only node
+ // (as node_needs_gamma_fix() would only return true in
+ // for an input in that case). That means we should insert
+ // a conversion node _after_ ourselves.
+ if (node->incoming_links.empty()) {
+ assert(node->outgoing_links.empty());
+ Node *conversion = add_node(new GammaExpansionEffect());
+ CHECK(conversion->effect->set_int("source_curve", node->output_gamma_curve));
+ conversion->output_gamma_curve = GAMMA_LINEAR;
+ connect_nodes(node, conversion);
+ }
+
+ // If not, go through each input that is not linear gamma,
+ // and insert a gamma conversion before it.
+ for (unsigned j = 0; j < node->incoming_links.size(); ++j) {
+ Node *input = node->incoming_links[j];
+ assert(input->output_gamma_curve != GAMMA_INVALID);
+ if (input->output_gamma_curve == GAMMA_LINEAR) {
+ continue;
+ }
+ Node *conversion = add_node(new GammaExpansionEffect());
+ CHECK(conversion->effect->set_int("source_curve", input->output_gamma_curve));
+ conversion->output_gamma_curve = GAMMA_LINEAR;
+ insert_node_between(input, conversion, node);
+ }
+
+ // Re-sort topologically, and propagate the new information.
+ propagate_gamma_and_color_space();
+
+ found_any = true;
+ break;
+ }
+
+ char filename[256];
+ sprintf(filename, "step%u-gammafix-iter%u.dot", step, ++gamma_propagation_pass);
+ output_dot(filename);
+ assert(gamma_propagation_pass < 100);
+ } while (found_any);
+
+ for (unsigned i = 0; i < nodes.size(); ++i) {
+ Node *node = nodes[i];
+ if (node->disabled) {
+ continue;
+ }
+ assert(node->output_gamma_curve != GAMMA_INVALID);
+ }
+}
+
+// Make so that the output is in the desired gamma.
+// Note that this assumes linear input gamma, so it might create the need
+// for another pass of fix_internal_gamma().
+void EffectChain::fix_output_gamma()
+{
+ Node *output = find_output_node();
+ if (output->output_gamma_curve != output_format.gamma_curve) {
+ Node *conversion = add_node(new GammaCompressionEffect());
+ CHECK(conversion->effect->set_int("destination_curve", output_format.gamma_curve));
+ conversion->output_gamma_curve = output_format.gamma_curve;
+ connect_nodes(output, conversion);
+ }
+}
+
+// If the user has requested dither, add a DitherEffect right at the end
+// (after GammaCompressionEffect etc.). This needs to be done after everything else,
+// since dither is about the only effect that can _not_ be done in linear space.
+void EffectChain::add_dither_if_needed()
+{
+ if (num_dither_bits == 0) {
+ return;
+ }
+ Node *output = find_output_node();
+ Node *dither = add_node(new DitherEffect());
+ CHECK(dither->effect->set_int("num_bits", num_dither_bits));
+ connect_nodes(output, dither);
+
+ dither_effect = dither->effect;
+}
+
+// Find the output node. This is, simply, one that has no outgoing links.
+// If there are multiple ones, the graph is malformed (we do not support
+// multiple outputs right now).
+Node *EffectChain::find_output_node()
+{