#include "core/chain.hpp" namespace effector { std::vector enabledStages(const std::vector& stages) { std::vector running; running.reserve(stages.size()); for (const auto& stage : stages) { if (stage.enabled && stage.effect) running.push_back(&stage); } return running; } ParamValues stageValues(const ChainStage& stage, const StyleSet& styles) { ParamValues values = stage.effect->defaults(); for (const auto& [key, value] : stage.values) values[key] = value; const Style* style = findStyle(styles, stage.effect->styleKind(), stage.style); if (style != nullptr) { // Only the params the effect marked styled(): the words and the // placement stay the stage's own, which is what lets two stages share // one look without sharing a position. for (const auto& spec : stage.effect->params()) { if (!spec.inStyle) continue; const auto found = style->values.find(spec.key); if (found != style->values.end()) values[spec.key] = found->second; } } return values; } Image renderChain(const std::vector& stages, const std::vector& sources, int width, int height, Rng& rng, const StyleSet& styles) { if (stages.empty()) throw EffectError("The effect chain is empty."); for (const auto& stage : stages) { if (!stage.effect) throw EffectError("The effect chain has an empty stage."); } // A disabled stage never reaches the render at all: it is not handed an // image and draws nothing from the shared rng, so switching one off gives // back exactly the picture the chain made before it was added. const auto running = enabledStages(stages); if (running.empty()) throw EffectError("Every effect in the chain is disabled."); Image image; for (std::size_t i = 0; i < running.size(); ++i) { const auto& stage = *running[i]; const auto values = stageValues(stage, styles); if (i == 0) { image = stage.effect->render(sources, width, height, rng, values); } else { // Feeding the previous result back in as the only source is what // cascading means here; effects need to know nothing about it. const std::vector previous = {std::move(image)}; image = stage.effect->render(previous, width, height, rng, values); } } return image; } bool chainNeedsSources(const std::vector& stages) { const auto running = enabledStages(stages); if (running.empty()) return false; return running.front()->effect->needsSources(); } std::string chainTitle(const std::vector& stages) { std::string text; for (const auto* stage : enabledStages(stages)) { if (!text.empty()) text += " → "; text += stage->effect->title(); } return text; } std::string chainSlug(const std::vector& stages) { std::string text; for (const auto* stage : enabledStages(stages)) { if (!text.empty()) text += "-"; text += stage->effect->name(); } return text; } } // namespace effector