Cascade effects through a chain
renderChain runs a list of stages in order: stage 0 works on the loaded sources, every later one on the output of the stage before it, so blur after scanlines softens the scanlines rather than the photographs. The effects themselves are untouched — none of them knows whether its input came from disk. All stages share one Rng and run in sequence, so a seed still reproduces the cascade byte for byte. The sidebar grows an "Effect chain" list: add a stage from a menu built from the registry, reorder it, remove it, and edit the selected stage's params underneath. Each stage keeps its own values. The CLI takes the same chain from repeated -e, with every flag going to the stage whose effect declares it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+21
-4
@@ -4,6 +4,7 @@
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
|
||||
#include "core/chain.hpp"
|
||||
#include "core/effect.hpp"
|
||||
#include "core/registry.hpp"
|
||||
|
||||
@@ -48,7 +49,6 @@ std::string requireValue(int argc, char** argv, int& i, const std::string& flag)
|
||||
|
||||
Options parseOptions(int argc, char** argv) {
|
||||
Options options;
|
||||
options.effect = defaultEffectName();
|
||||
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
const std::string arg = argv[i];
|
||||
@@ -58,8 +58,9 @@ Options parseOptions(int argc, char** argv) {
|
||||
} else if (arg == "--list-effects") {
|
||||
options.listEffects = true;
|
||||
} else if (arg == "-e" || arg == "--effect") {
|
||||
options.effect = requireValue(argc, argv, i, arg);
|
||||
effectByName(options.effect); // validate now, fail early
|
||||
const std::string name = requireValue(argc, argv, i, arg);
|
||||
effectByName(name); // validate now, fail early
|
||||
options.effects.push_back(name);
|
||||
} else if (arg == "-o" || arg == "--output") {
|
||||
options.output = requireValue(argc, argv, i, arg);
|
||||
} else if (arg == "-s" || arg == "--size") {
|
||||
@@ -119,6 +120,7 @@ Options parseOptions(int argc, char** argv) {
|
||||
options.images.push_back(arg);
|
||||
}
|
||||
}
|
||||
if (options.effects.empty()) options.effects.push_back(defaultEffectName());
|
||||
return options;
|
||||
}
|
||||
|
||||
@@ -127,7 +129,7 @@ std::string usage(const char* programName) {
|
||||
out << "Usage: " << programName << " [options] [image ...]\n\n"
|
||||
<< "Image effects app. Runs the GTK interface unless --output is given.\n\n"
|
||||
<< "Options:\n"
|
||||
<< " -e, --effect NAME effect to apply (default: " << defaultEffectName() << ")\n"
|
||||
<< " -e, --effect NAME effect to apply, repeat to cascade (default: " << defaultEffectName() << ")\n"
|
||||
<< " -o, --output FILE render headlessly to this file and exit\n"
|
||||
<< " -s, --size WxH canvas size (default: 1920x1080)\n"
|
||||
<< " --seed N random seed (default: random)\n"
|
||||
@@ -147,4 +149,19 @@ std::string usage(const char* programName) {
|
||||
return out.str();
|
||||
}
|
||||
|
||||
std::vector<ChainStage> buildChain(const Options& options) {
|
||||
std::vector<ChainStage> stages;
|
||||
for (const auto& name : options.effects) {
|
||||
ChainStage stage;
|
||||
stage.effect = effectByName(name);
|
||||
stage.values = stage.effect->defaults();
|
||||
for (const auto& spec : stage.effect->params()) {
|
||||
const auto it = options.params.find(spec.key);
|
||||
if (it != options.params.end()) stage.values[spec.key] = it->second;
|
||||
}
|
||||
stages.push_back(std::move(stage));
|
||||
}
|
||||
return stages;
|
||||
}
|
||||
|
||||
} // namespace effector
|
||||
|
||||
+7
-1
@@ -5,13 +5,14 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "core/chain.hpp"
|
||||
#include "core/params.hpp"
|
||||
|
||||
namespace effector {
|
||||
|
||||
struct Options {
|
||||
std::vector<std::string> images;
|
||||
std::string effect;
|
||||
std::vector<std::string> effects; // one per cascade stage, in order
|
||||
std::string output; // empty -> launch the interface
|
||||
int width = 1920;
|
||||
int height = 1080;
|
||||
@@ -27,4 +28,9 @@ Options parseOptions(int argc, char** argv);
|
||||
|
||||
std::string usage(const char* programName);
|
||||
|
||||
// Turns the parsed names and flags into cascade stages: every flag goes to the
|
||||
// stage whose effect declares it, so --strip-height and --radius can be given
|
||||
// together for `-e scanlines -e blur`.
|
||||
std::vector<ChainStage> buildChain(const Options& options);
|
||||
|
||||
} // namespace effector
|
||||
|
||||
Reference in New Issue
Block a user